Table Visualization and display text changes. (#6382)

- Missing tests from number parsing.
- Fix type signature on some warning methods.
- Fix warnings on `Standard.Database.Data.Table.parse_values`.
- Added test for `Nothing` and empty string on `use_first_row_as_names`.
- New API for `Number.format` taking a simple format string and `Locale`.
- Add ellipsis to truncated `Text.to_display_text`.
- Adjusted built-in `to_display_text` for numbers to not include type (but also to display BigInteger as value).
- Remove `Noise.Generator` interface type.
- Json: Added `to_display_text` to `JS_Object`.
- Time: Added `to_display_text` for `Date`, `Time_Of_Day`, `Date_Time`, `Duration` and `Period`.
- Text: Added `to_display_text` to `Locale`, `Case_Sensitivity`, `Encoding`, `Text_Sub_Range`, `Span`, `Utf_16_Span`.
- System: Added `to_display_text` to `File`, `File_Permissions`, `Process_Result` and `Exit_Code`.
- Network: Added `to_display_text` to `URI`, `HTTP_Status_Code` and `Header`.
- Added `to_display_text` to `Maybe`, `Regression`, `Pair`, `Range`, `Filter_Condition`.
- Added support for `to_js_object` and `to_display_text` to `Random_Number_Generator`.
- Verified all error types have `to_display_text`.
- Removed `BigInt`, `Date`, `Date_Time` and `Time_Of_Day` JS based rendering as using `to_display_text` now.
- Added support for rendering nested structures in the table viz.
This commit is contained in:
James Dunkerley 2023-04-26 19:15:48 +01:00 committed by GitHub
parent 0e5ee35aba
commit 0e51131809
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 655 additions and 198 deletions

8
.github/CODEOWNERS vendored
View File

@ -29,16 +29,16 @@ Cargo.toml
# Engine (old) # Engine (old)
# This section should be removed once the engine moves to /app/engine # This section should be removed once the engine moves to /app/engine
/build.sbt @4e6 @jaroslavtulach @hubertp /build.sbt @4e6 @jaroslavtulach @hubertp
/distribution/ @4e6 @jdunkerley @radeusgd /distribution/ @4e6 @jdunkerley @radeusgd @GregoryTravis
/engine/ @4e6 @jaroslavtulach @hubertp /engine/ @4e6 @jaroslavtulach @hubertp
/project/ @4e6 @jaroslavtulach @hubertp /project/ @4e6 @jaroslavtulach @hubertp
/test/ @jdunkerley @radeusgd /test/ @jdunkerley @radeusgd @GregoryTravis
/tools/ @4e6 @jaroslavtulach @radeusgd /tools/ @4e6 @jaroslavtulach @radeusgd
# Enso Libraries # Enso Libraries
# This section should be amended once the engine moves to /app/engine # This section should be amended once the engine moves to /app/engine
/distribution/lib/ @jdunkerley @radeusgd /distribution/lib/ @jdunkerley @radeusgd @GregoryTravis
/std-bits/ @jdunkerley @radeusgd /std-bits/ @jdunkerley @radeusgd @GregoryTravis
# Cloud Dashboard & Authentication # Cloud Dashboard & Authentication
/app/ide-desktop/lib/dashboard @PabloBuchu @indiv0 @somebody1234 /app/ide-desktop/lib/dashboard @PabloBuchu @indiv0 @somebody1234

View File

@ -32,6 +32,10 @@ class TableVisualization extends Visualization {
} }
onDataReceived(data) { onDataReceived(data) {
function addRowIndex(data) {
return data.map((row, i) => ({ ['#']: i, ...row }))
}
function hasExactlyKeys(keys, obj) { function hasExactlyKeys(keys, obj) {
return Object.keys(obj).length === keys.length && keys.every(k => obj.hasOwnProperty(k)) return Object.keys(obj).length === keys.length && keys.every(k => obj.hasOwnProperty(k))
} }
@ -65,44 +69,8 @@ class TableVisualization extends Visualization {
if (content instanceof Object) { if (content instanceof Object) {
const type = content.type const type = content.type
if (type === 'BigInt') { if (content['_display_text_']) {
return BigInt(content.value)
} else if (content['_display_text_']) {
return content['_display_text_'] return content['_display_text_']
} else if (type === 'Date') {
return new Date(content.year, content.month - 1, content.day)
.toISOString()
.substring(0, 10)
} else if (type === 'Time_Of_Day') {
const js_date = new Date(
0,
0,
1,
content.hour,
content.minute,
content.second,
content.nanosecond / 1000000
)
return (
js_date.toTimeString().substring(0, 8) +
(js_date.getMilliseconds() === 0 ? '' : '.' + js_date.getMilliseconds())
)
} else if (type === 'Date_Time') {
const js_date = new Date(
content.year,
content.month - 1,
content.day,
content.hour,
content.minute,
content.second,
content.nanosecond / 1000000
)
return (
js_date.toISOString().substring(0, 10) +
' ' +
js_date.toTimeString().substring(0, 8) +
(js_date.getMilliseconds() === 0 ? '' : '.' + js_date.getMilliseconds())
)
} else { } else {
return `{ ${type} Object }` return `{ ${type} Object }`
} }
@ -136,7 +104,7 @@ class TableVisualization extends Visualization {
sortable: true, sortable: true,
filter: true, filter: true,
resizable: true, resizable: true,
minWidth: 50, minWidth: 25,
headerValueGetter: params => params.colDef.field, headerValueGetter: params => params.colDef.field,
}, },
onColumnResized: e => this.lockColumnSize(e), onColumnResized: e => this.lockColumnSize(e),
@ -158,22 +126,49 @@ class TableVisualization extends Visualization {
}, },
]) ])
this.agGridOptions.api.setRowData([{ Error: parsedData.error }]) this.agGridOptions.api.setRowData([{ Error: parsedData.error }])
} else if (parsedData.json != null && isMatrix(parsedData.json)) { } else if (parsedData.type === 'Matrix') {
columnDefs = parsedData.json[0].map((_, i) => ({ field: i.toString() })) let defs = [{ field: '#' }]
rowData = parsedData.json for (let i = 0; i < parsedData.column_count; i++) {
defs.push({ field: i.toString() })
}
columnDefs = defs
rowData = addRowIndex(parsedData.json)
dataTruncated = parsedData.all_rows_count !== parsedData.json.length dataTruncated = parsedData.all_rows_count !== parsedData.json.length
} else if (parsedData.json != null && isObjectMatrix(parsedData.json)) { } else if (parsedData.type === 'Object_Matrix') {
let firstKeys = Object.keys(parsedData.json[0]) let defs = [{ field: '#' }]
let keys = {}
parsedData.json.forEach(val => {
if (val) {
Object.keys(val).forEach(k => {
if (!keys[k]) {
keys[k] = true
defs.push({ field: k })
}
})
}
})
columnDefs = defs
rowData = addRowIndex(parsedData.json)
dataTruncated = parsedData.all_rows_count !== parsedData.json.length
} else if (isMatrix(parsedData.json)) {
// Kept to allow visualization from older versions of the backend.
columnDefs = [
{ field: '#' },
...parsedData.json[0].map((_, i) => ({ field: i.toString() })),
]
rowData = addRowIndex(parsedData.json)
dataTruncated = parsedData.all_rows_count !== parsedData.json.length
} else if (isObjectMatrix(parsedData.json)) {
// Kept to allow visualization from older versions of the backend.
let firstKeys = [{ field: '#' }, ...Object.keys(parsedData.json[0])]
columnDefs = firstKeys.map(field => ({ field })) columnDefs = firstKeys.map(field => ({ field }))
rowData = parsedData.json.map(obj => rowData = addRowIndex(parsedData.json)
firstKeys.reduce((acc, key) => ({ ...acc, [key]: toRender(obj[key]) }), {})
)
dataTruncated = parsedData.all_rows_count !== parsedData.json.length dataTruncated = parsedData.all_rows_count !== parsedData.json.length
} else if (parsedData.json != null && Array.isArray(parsedData.json)) { } else if (Array.isArray(parsedData.json)) {
columnDefs = [{ field: '#' }, { field: 'Value' }] columnDefs = [{ field: '#' }, { field: 'Value' }]
rowData = parsedData.json.map((row, i) => ({ ['#']: i, Value: toRender(row) })) rowData = parsedData.json.map((row, i) => ({ ['#']: i, Value: toRender(row) }))
dataTruncated = parsedData.all_rows_count !== parsedData.json.length dataTruncated = parsedData.all_rows_count !== parsedData.json.length
} else if (parsedData.json != null) { } else if (parsedData.json !== undefined) {
columnDefs = [{ field: 'Value' }] columnDefs = [{ field: 'Value' }]
rowData = [{ Value: toRender(parsedData.json) }] rowData = [{ Value: toRender(parsedData.json) }]
} else { } else {

View File

@ -199,6 +199,37 @@ type Filter_Condition
Is_In values -> values.contains Is_In values -> values.contains
Not_In values -> elem -> values.contains elem . not Not_In values -> elem -> values.contains elem . not
## PRIVATE
Convert to a display representation of this Filter_Condition.
to_display_text : Text
to_display_text self =
render_case case_sensitivity =
if case_sensitivity == Case_Sensitivity.Default then "" else " Case " + case_sensitivity.to_display_text
condition = case self of
Less value -> "<" + value.to_display_text
Equal_Or_Less value -> "<=" + value.to_display_text
Equal value -> "==" + value.to_display_text
Equal_Or_Greater value -> ">=" + value.to_display_text
Greater value -> ">" + value.to_display_text
Not_Equal value -> "!=" + value.to_display_text
Between lower upper -> "Between " + lower.to_display_text + " And " + upper.to_display_text
Starts_With prefix case_sensitivity -> "Starts With " + prefix.to_display_text + (render_case case_sensitivity)
Ends_With suffix case_sensitivity -> "Ends With " + suffix.to_display_text + (render_case case_sensitivity)
Contains substring case_sensitivity -> "Contains " + substring.to_display_text + (render_case case_sensitivity)
Not_Contains substring case_sensitivity -> "Not Contains " + substring.to_display_text + (render_case case_sensitivity)
Is_Nothing -> "is Nothing"
Not_Nothing -> "is Not Nothing"
Is_True -> "is True"
Is_False -> "is False"
Is_Empty -> "is Empty"
Not_Empty -> "is Not Empty"
Like sql_pattern -> "Like " + sql_pattern.to_display_text
Not_Like sql_pattern -> "Not Like " + sql_pattern.to_display_text
Is_In values -> "is in " + values.to_display_text
Not_In values -> "is not in " + values.to_display_text
"Filter Condition: " + condition
## PRIVATE ## PRIVATE
Gets a widget set up for a Filter_Condition. Gets a widget set up for a Filter_Condition.
default_widget = default_widget =

View File

@ -2,6 +2,7 @@ import project.Any.Any
import project.Data.Numbers.Integer import project.Data.Numbers.Integer
import project.Data.Range.Range import project.Data.Range.Range
import project.Data.Range.Extensions import project.Data.Range.Extensions
import project.Data.Text.Text
import project.Data.Vector.Vector import project.Data.Vector.Vector
import project.Errors.Common.Index_Out_Of_Bounds import project.Errors.Common.Index_Out_Of_Bounds
import project.Error.Error import project.Error.Error
@ -55,6 +56,17 @@ type Index_Sub_Range
input, an error is raised. input, an error is raised.
Every (step:Integer) (first:Integer=0) Every (step:Integer) (first:Integer=0)
## PRIVATE
Convert to a display representation of this Index_Sub_Range.
to_display_text : Text
to_display_text self = case self of
Index_Sub_Range.First count -> "First " + count.to_display_text
Index_Sub_Range.Last count -> "Last " + count.to_display_text
Index_Sub_Range.While f -> "While " + f.to_display_text
Index_Sub_Range.By_Index indexes -> "By_Index " + indexes.to_display_text
Index_Sub_Range.Sample count _ -> "Sample " + count.to_display_text
Index_Sub_Range.Every step first -> "Every " + step.to_display_text + (if first == 0 then "" else " from " + first.to_display_text)
## PRIVATE ## PRIVATE
Resolves a vector of ranges or indices into a vector of ranges that fit Resolves a vector of ranges or indices into a vector of ranges that fit
within a sequence. within a sequence.

View File

@ -1,4 +1,5 @@
import project.Data.Numbers.Number import project.Data.Numbers.Number
import project.Data.Text.Text
from project.Data.Boolean import Boolean, False from project.Data.Boolean import Boolean, False
@ -136,3 +137,15 @@ type Interval
example_not_empty = Interval.inclusive 0 0.001 . not_empty example_not_empty = Interval.inclusive 0 0.001 . not_empty
not_empty : Boolean not_empty : Boolean
not_empty self = self.is_empty.not not_empty self = self.is_empty.not
## PRIVATE
Convert to a display representation of this Interval.
to_display_text : Text
to_display_text self =
prefix = case self.start of
Bound.Exclusive s -> "(" + s.to_display_text + ", "
Bound.Inclusive s -> "[" + s.to_display_text + ", "
suffix = case self.end of
Bound.Exclusive e -> e.to_display_text + ")"
Bound.Inclusive e -> e.to_display_text + "]"
prefix + suffix

View File

@ -180,6 +180,12 @@ type JS_Object
to_text : Text to_text : Text
to_text self = Json.stringify self to_text self = Json.stringify self
## PRIVATE
Convert JS_Object to a friendly string.
to_display_text : Text
to_display_text self =
self.to_text.to_display_text
## Convert to a JSON representation. ## Convert to a JSON representation.
to_json : Text to_json : Text
to_json self = self.to_text to_json self = self.to_text

View File

@ -413,4 +413,11 @@ type Locale
## PRIVATE ## PRIVATE
Converts the locale to text. Converts the locale to text.
to_text : Text | Nothing to_text : Text | Nothing
to_text self = self.java_locale.toLanguageTag to_text self =
tag = self.java_locale.toLanguageTag
if tag == "" then "Default" else tag
## PRIVATE
Convert Locale to a friendly string.
to_display_text : Text
to_display_text self = "Locale(" + self.to_text + ")"

View File

@ -1,4 +1,5 @@
import project.Any.Any import project.Any.Any
import project.Data.Text.Text
from project.Data.Boolean import Boolean, True, False from project.Data.Boolean import Boolean, True, False
@ -54,5 +55,13 @@ type Maybe
Maybe.None -> False Maybe.None -> False
Maybe.Some _ -> True Maybe.Some _ -> True
## Check if the maybe value is `None`.
is_none : Boolean is_none : Boolean
is_none self = self.is_some.not is_none self = self.is_some.not
## PRIVATE
Convert Maybe to a friendly string.
to_display_text : Text
to_display_text self = case self of
Maybe.None -> "None"
Maybe.Some val -> "Some(" + val.to_display_text + ")"

View File

@ -7,28 +7,7 @@ import project.Errors.Unimplemented.Unimplemented
polyglot java import java.lang.Long polyglot java import java.lang.Long
polyglot java import java.util.Random polyglot java import java.util.Random
## PRIVATE ## A noise generator that implements a seeded deterministic random perturbation
The interface for the noise generator abstraction.
To be a valid generator, it must provide the `step` method as described
below.
type Generator
## PRIVATE
Step the generator to produce the next value..
Arguments:
- The input number, which is intended for use as a seed.
- A range for output values, which should range over the chosen output
type.
The return type may be chosen freely by the generator implementation, as
it usually depends on the generator and its intended use.
step : Number -> Interval -> Any
step self _ _ = Unimplemented.throw "Only intended to demonstrate an interface."
## A noise generator that implements a seeded deterministic random peterbation
of the input. of the input.
It produces what is commonly termed "white" noise, where any value in the It produces what is commonly termed "white" noise, where any value in the
@ -73,6 +52,6 @@ type Deterministic_Random
> Example > Example
Deterministically perturb the input number 1. Deterministically perturb the input number 1.
1.noise 1.noise
Number.noise : Interval -> Generator -> Any Number.noise : Interval -> Deterministic_Random -> Any
Number.noise self (interval = Interval.exclusive 0 1) gen=Deterministic_Random = Number.noise self (interval = Interval.exclusive 0 1) gen=Deterministic_Random =
gen.step self interval gen.step self interval

View File

@ -10,9 +10,10 @@ from project.Data.Boolean import Boolean, True, False
polyglot java import java.lang.Double polyglot java import java.lang.Double
polyglot java import java.lang.Math polyglot java import java.lang.Math
polyglot java import java.lang.String
polyglot java import java.lang.Long polyglot java import java.lang.Long
polyglot java import java.lang.NumberFormatException polyglot java import java.lang.NumberFormatException
polyglot java import java.text.DecimalFormat
polyglot java import java.text.DecimalFormatSymbols
polyglot java import java.text.NumberFormat polyglot java import java.text.NumberFormat
polyglot java import java.text.ParseException polyglot java import java.text.ParseException
@ -263,20 +264,21 @@ type Number
log : Number -> Decimal log : Number -> Decimal
log self base = self.ln / base.ln log self base = self.ln / base.ln
## UNSTABLE This API is not user-friendly and will be improved in the future. ## Converts a numeric value to a string, using the Java DecimalFormat
formatter.
Converts a numeric value to a string, using the Java string formatting
syntax.
Arguments: Arguments:
- fmt: The java-style formatting specifier. - format: The java-style formatting specifier.
> Example > Example
Convert the value 5 to a string. Convert the value 5000 to a string.
5.format "%x" 5000.format "#,##0"
format : Text -> Text format : Text -> Locale -> Text
format self fmt = String.format fmt self format self format locale=Locale.default =
symbols = DecimalFormatSymbols.new locale.java_locale
formatter = DecimalFormat.new format symbols
formatter.format self
## Checks equality of numbers, using an `epsilon` value. ## Checks equality of numbers, using an `epsilon` value.

View File

@ -1,6 +1,8 @@
import project.Any.Any import project.Any.Any
import project.Data.Boolean.Boolean import project.Data.Boolean.Boolean
import project.Data.Numbers.Integer import project.Data.Numbers.Integer
import project.Data.Text.Text
import project.Data.Text.Extensions
import project.Data.Vector.Vector import project.Data.Vector.Vector
import project.Errors.Common.Index_Out_Of_Bounds import project.Errors.Common.Index_Out_Of_Bounds
import project.Errors.Common.Not_Found import project.Errors.Common.Not_Found
@ -248,6 +250,17 @@ type Pair
f self.second f self.second
Nothing Nothing
## PRIVATE
Convert to a display representation of this Pair.
to_display_text : Text
to_display_text self =
first = self.first.to_display_text
second = self.second.to_display_text
if first.length + second.length < 73 then "Pair(" + first + ", " + second + ")" else
first_trim = if first.length > 36 then first.take 34 + " …" else first
second_trim = if second.length > 36 then second.take 34 + " …" else second
"Pair(" + first_trim + ", " + second_trim + ")"
## PRIVATE ## PRIVATE
check_start_valid start function max=3 = check_start_valid start function max=3 =
used_start = if start < 0 then start + 2 else start used_start = if start < 0 then start + 2 else start

View File

@ -430,6 +430,14 @@ type Range
@Tail_Call fold_function (function current value) (value + self.step) @Tail_Call fold_function (function current value) (value + self.step)
fold_function self.start self.start+self.step fold_function self.start self.start+self.step
## PRIVATE
Convert to a display representation of this Range.
to_display_text : Text
to_display_text self =
start = "[" + self.start.to_display_text + " .. " + self.end.to_display_text
step = if self.step.abs == 1 then "" else " by " + self.step.to_display_text
start + step + "]"
## PRIVATE ## PRIVATE
throw_zero_step_error = Error.throw (Illegal_State.Error "A range with step = 0 is ill-formed.") throw_zero_step_error = Error.throw (Illegal_State.Error "A range with step = 0 is ill-formed.")

View File

@ -24,6 +24,17 @@ type Model
## Fit a power series (y = A x ^ B) to the data. ## Fit a power series (y = A x ^ B) to the data.
Power Power
## PRIVATE
Convert to a display representation of this Model.
to_display_text : Text
to_display_text self = case self of
Model.Linear intercept ->
if intercept.is_nothing then "Linear" else "Linear(intercept: " + intercept.to_display_text + ")"
Model.Exponential intercept ->
if intercept.is_nothing then "Exponential" else "Exponential(intercept: " + intercept.to_display_text + ")"
Model.Logarithmic -> "Logarithmic"
Model.Power -> "Power"
## PRIVATE ## PRIVATE
Computes the R Squared value for a model and returns a new instance. Computes the R Squared value for a model and returns a new instance.
fitted_model_with_r_squared : Any -> Number -> Number -> Vector -> Vector -> Fitted_Model fitted_model_with_r_squared : Any -> Number -> Number -> Vector -> Vector -> Fitted_Model
@ -33,7 +44,6 @@ type Model
constructor a b r_squared constructor a b r_squared
## PRIVATE ## PRIVATE
Computes the natural log series as long as all values are positive. Computes the natural log series as long as all values are positive.
ln_series : Vector -> Text -> Vector ! Illegal_Argument ln_series : Vector -> Text -> Vector ! Illegal_Argument
ln_series xs series_name="Values" = ln_series xs series_name="Values" =
@ -82,12 +92,16 @@ type Fitted_Model
Display the fitted line. Display the fitted line.
to_text : Text to_text : Text
to_text self = to_text self =
equation = case self of "Fitted_Model(" + self.to_display_text + ")"
Fitted_Model.Linear slope intercept _ -> slope.to_text + " * X + " + intercept.to_text
Fitted_Model.Exponential a b _ -> a.to_text + " * (" + b.to_text + " * X).exp" ## PRIVATE
Fitted_Model.Logarithmic a b _ -> a.to_text + " * X.ln + " + b.to_text Convert to a display representation of this Fitted_Model.
Fitted_Model.Power a b _ -> a.to_text + " * X ^ " + b.to_text to_display_text : Text
"Fitted_Model(" + equation + ")" to_display_text self = case self of
Fitted_Model.Linear slope intercept _ -> slope.to_text + " * X + " + intercept.to_text
Fitted_Model.Exponential a b _ -> a.to_text + " * (" + b.to_text + " * X).exp"
Fitted_Model.Logarithmic a b _ -> a.to_text + " * X.ln + " + b.to_text
Fitted_Model.Power a b _ -> a.to_text + " * X ^ " + b.to_text
## Use the model to predict a value. ## Use the model to predict a value.
predict : Number -> Number predict : Number -> Number
@ -108,7 +122,6 @@ type Fit_Error
Error message Error message
## PRIVATE ## PRIVATE
Converts the `Fit_Error` to a human-readable representation. Converts the `Fit_Error` to a human-readable representation.
to_display_text : Text to_display_text : Text
to_display_text self = "Could not fit the model: " + self.message.to_text to_display_text self = "Could not fit the model: " + self.message.to_text

View File

@ -23,6 +23,15 @@ type Case_Sensitivity
- locale: The locale used for the comparison. - locale: The locale used for the comparison.
Insensitive locale=Locale.default Insensitive locale=Locale.default
## PRIVATE
Convert Case_Sensitivity to a friendly string.
to_display_text : Text
to_display_text self = case self of
Case_Sensitivity.Default -> "Default"
Case_Sensitivity.Sensitive -> "Sensitive"
Case_Sensitivity.Insensitive locale ->
if locale == Locale.default then "Insensitive" else "Insensitive(" + locale.to_text + ")"
## PRIVATE ## PRIVATE
Creates a Java `TextFoldingStrategy` from the case sensitivity setting. Creates a Java `TextFoldingStrategy` from the case sensitivity setting.
folding_strategy : Case_Sensitivity -> TextFoldingStrategy folding_strategy : Case_Sensitivity -> TextFoldingStrategy
@ -43,7 +52,7 @@ type Case_Sensitivity
Case_Sensitivity.Insensitive locale -> case locale == Locale.default of Case_Sensitivity.Insensitive locale -> case locale == Locale.default of
True -> True True -> True
False -> False ->
msg = "Custom locales are not supported for this operationc." msg = "Custom locales are not supported for this operation."
Error.throw (Illegal_Argument.Error msg) Error.throw (Illegal_Argument.Error msg)
## PRIVATE ## PRIVATE

View File

@ -107,3 +107,8 @@ type Encoding
## Encoding for Vietnamese (Windows). ## Encoding for Vietnamese (Windows).
windows_1258 : Encoding windows_1258 : Encoding
windows_1258 = Encoding.Value "windows-1258" windows_1258 = Encoding.Value "windows-1258"
## PRIVATE
Convert Encoding to a friendly string.
to_display_text : Text
to_display_text self = self.character_set

View File

@ -78,3 +78,8 @@ type Regex_Syntax_Error
Arguments: Arguments:
- message: A description of the erroneous syntax. - message: A description of the erroneous syntax.
Error message Error message
## PRIVATE
Provides a human-readable representation of the `Regex_Syntax_Error`.
to_display_text : Text
to_display_text self = "Regex Syntax Error:" + self.message

View File

@ -95,6 +95,12 @@ type Span
to_utf_16_span self = to_utf_16_span self =
Utf_16_Span.Value (range_to_char_indices self.parent self.range) self.parent Utf_16_Span.Value (range_to_char_indices self.parent self.range) self.parent
## PRIVATE
Convert to a display representation of this Span.
to_display_text : Text
to_display_text self = self.text
type Utf_16_Span type Utf_16_Span
## A representation of a span of UTF-16 code units in Enso's `Text` type. ## A representation of a span of UTF-16 code units in Enso's `Text` type.
@ -165,6 +171,11 @@ type Utf_16_Span
grapheme_end = grapheme_last + 1 grapheme_end = grapheme_last + 1
Span.Value (grapheme_first.up_to grapheme_end) self.parent Span.Value (grapheme_first.up_to grapheme_end) self.parent
## PRIVATE
Convert to a display representation of this Span.
to_display_text : Text
to_display_text self = self.text
## PRIVATE ## PRIVATE
Utility function taking a range pointing at grapheme clusters and converting Utility function taking a range pointing at grapheme clusters and converting
to a range on the underlying code units. to a range on the underlying code units.

View File

@ -1,5 +1,6 @@
import project.Data.Locale.Locale import project.Data.Locale.Locale
import project.Data.Text.Case_Sensitivity.Case_Sensitivity import project.Data.Text.Case_Sensitivity.Case_Sensitivity
import project.Data.Text.Text
import project.Nothing.Nothing import project.Nothing.Nothing
from project.Data.Boolean import Boolean, True, False from project.Data.Boolean import Boolean, True, False
@ -46,3 +47,9 @@ type Text_Ordering
Text_Ordering.Default _ -> Case_Sensitivity.Default Text_Ordering.Default _ -> Case_Sensitivity.Default
Text_Ordering.Case_Sensitive _ -> Case_Sensitivity.Sensitive Text_Ordering.Case_Sensitive _ -> Case_Sensitivity.Sensitive
Text_Ordering.Case_Insensitive locale _ -> Case_Sensitivity.Insensitive locale Text_Ordering.Case_Insensitive locale _ -> Case_Sensitivity.Insensitive locale
## PRIVATE
Convert Text_Ordering to a friendly string.
to_display_text : Text
to_display_text self =
self.case_sensitivity.to_display_text + if self.sort_digits_as_numbers then " (Natural Order)" else ""

View File

@ -40,6 +40,15 @@ type Text_Sub_Range
Select an empty string if the input does not contain `delimiter`. Select an empty string if the input does not contain `delimiter`.
After_Last (delimiter : Text) After_Last (delimiter : Text)
## PRIVATE
Convert to a display representation of this Index_Sub_Range.
to_display_text : Text
to_display_text self = case self of
Text_Sub_Range.Before delimiter -> "Before " + delimiter.to_display_text
Text_Sub_Range.Before_Last delimiter -> "Before Last " + delimiter.to_display_text
Text_Sub_Range.After delimiter -> "After " + delimiter.to_display_text
Text_Sub_Range.After_Last delimiter -> "After Last " + delimiter.to_display_text
type Codepoint_Ranges type Codepoint_Ranges
## PRIVATE ## PRIVATE
A list of codepoint ranges corresponding to the matched parts of the A list of codepoint ranges corresponding to the matched parts of the

View File

@ -543,6 +543,12 @@ type Date
_ -> _ ->
Error.throw (Illegal_Argument.Error "Illegal period argument") Error.throw (Illegal_Argument.Error "Illegal period argument")
## PRIVATE
Convert to a display representation of this Date.
to_display_text : Text
to_display_text self =
self.format "yyyy-MM-dd"
## PRIVATE ## PRIVATE
Convert to a JS_Object representing this Date. Convert to a JS_Object representing this Date.
@ -596,7 +602,6 @@ type Date
format : Text -> Text format : Text -> Text
format self pattern = Time_Utils.local_date_format self pattern format self pattern = Time_Utils.local_date_format self pattern
## PRIVATE ## PRIVATE
week_days_between start end = week_days_between start end =
## We split the interval into 3 periods: the first week (containing the ## We split the interval into 3 periods: the first week (containing the

View File

@ -589,6 +589,13 @@ type Date_Time
Time_Utils.datetime_adjust self Time_Utils.AdjustOp.MINUS period.internal_period Time_Utils.datetime_adjust self Time_Utils.AdjustOp.MINUS period.internal_period
ensure_in_epoch result result ensure_in_epoch result result
## PRIVATE
Convert to a display representation of this Date_Time.
to_display_text : Text
to_display_text self =
time_format = if self.nanosecond == 0 then "HH:mm:ss" else "HH:mm:ss.n"
self.format "yyyy-MM-dd "+time_format+" VV"
## PRIVATE ## PRIVATE
Convert to a JavaScript Object representing a Date_Time. Convert to a JavaScript Object representing a Date_Time.

View File

@ -4,6 +4,8 @@ import project.Data.Numbers.Decimal
import project.Data.Numbers.Integer import project.Data.Numbers.Integer
import project.Data.Ordering.Comparable import project.Data.Ordering.Comparable
import project.Data.Pair.Pair import project.Data.Pair.Pair
import project.Data.Text.Extensions
import project.Data.Text.Text
import project.Data.Time.Date_Time.Date_Time import project.Data.Time.Date_Time.Date_Time
import project.Data.Time.Period.Period import project.Data.Time.Period.Period
import project.Data.Vector.Vector import project.Data.Vector.Vector
@ -262,3 +264,17 @@ type Duration
if self.milliseconds==0 . not then b.append ["milliseconds", self.milliseconds] if self.milliseconds==0 . not then b.append ["milliseconds", self.milliseconds]
if self.nanoseconds==0 . not then b.append ["nanoseconds", self.nanoseconds] if self.nanoseconds==0 . not then b.append ["nanoseconds", self.nanoseconds]
JS_Object.from_pairs b.to_vector JS_Object.from_pairs b.to_vector
## PRIVATE
Convert Duration to a friendly string.
to_display_text : Text
to_display_text self = if self == Duration.zero then "0s" else
h = if self.hours == 0 then "" else self.hours.to_text + "h "
s = if self.seconds == 0 && self.nanoseconds == 0 then "" else
seconds = self.seconds + self.nanoseconds/1000000000
seconds.format "00.##########" + "s"
m = if self.minutes == 0 && (h=="" || s=="") then "" else self.minutes.to_text + "m "
(h+m+s).trim

View File

@ -1,12 +1,14 @@
import project.Any.Any import project.Any.Any
import project.Data.Numbers.Integer import project.Data.Numbers.Integer
import project.Data.Ordering.Comparable
import project.Data.Text.Extensions
import project.Data.Text.Text
import project.Data.Time.Date.Date import project.Data.Time.Date.Date
import project.Data.Time.Duration.Duration import project.Data.Time.Duration.Duration
import project.Data.Ordering.Comparable
import project.Data.Text.Text
import project.Error.Error import project.Error.Error
import project.Errors.Illegal_Argument.Illegal_Argument import project.Errors.Illegal_Argument.Illegal_Argument
import project.Errors.Time_Error.Time_Error import project.Errors.Time_Error.Time_Error
import project.Math
import project.Meta import project.Meta
import project.Nothing.Nothing import project.Nothing.Nothing
import project.Panic.Panic import project.Panic.Panic
@ -128,3 +130,17 @@ type Period
case err of case err of
DateTimeException -> Error.throw Time_Error.Error "Period subtraction failed" DateTimeException -> Error.throw Time_Error.Error "Period subtraction failed"
ArithmeticException -> Error.throw Illegal_Argument.Error "Arithmetic error" ArithmeticException -> Error.throw Illegal_Argument.Error "Arithmetic error"
## PRIVATE
Convert Period to a friendly string.
to_display_text : Text
to_display_text self = if self == Period.new then "0D" else
years = self.years + (self.months/12).floor
y = if years == 0 then "" else years.to_text + "Y "
d = if self.days == 0 then "" else self.days.to_text + "D "
months = self.months % 12
m = if months == 0 && (y=="" || d=="") then "" else months.to_text + "M "
(y + m + d) . trim

View File

@ -300,6 +300,13 @@ type Time_Of_Day
cons_pair = ["constructor", "new"] cons_pair = ["constructor", "new"]
JS_Object.from_pairs [type_pair, cons_pair, ["hour", self.hour], ["minute", self.minute], ["second", self.second], ["nanosecond", self.nanosecond]] JS_Object.from_pairs [type_pair, cons_pair, ["hour", self.hour], ["minute", self.minute], ["second", self.second], ["nanosecond", self.nanosecond]]
## PRIVATE
Convert to a display representation of this Time_Of_Day.
to_display_text : Text
to_display_text self =
if self.nanosecond == 0 then self.format "HH:mm:ss" else
self.format "HH:mm:ss.n"
## Format this time of day using the provided formatter pattern. ## Format this time of day using the provided formatter pattern.
Arguments: Arguments:

View File

@ -170,3 +170,8 @@ type Time_Zone
type_pair = ["type", "Time_Zone"] type_pair = ["type", "Time_Zone"]
cons_pair = ["constructor", "new"] cons_pair = ["constructor", "new"]
JS_Object.from_pairs [type_pair, cons_pair, ["id", self.zone_id]] JS_Object.from_pairs [type_pair, cons_pair, ["id", self.zone_id]]
## PRIVATE
Convert to a display representation of this Time_Zone.
to_display_text : Text
to_display_text self = self.to_text

View File

@ -3,6 +3,7 @@ import project.Error.Error
import project.Meta import project.Meta
import project.Nothing.Nothing import project.Nothing.Nothing
import project.Panic.Panic import project.Panic.Panic
from project.Data.Boolean import True, False from project.Data.Boolean import True, False
polyglot java import java.lang.ClassCastException polyglot java import java.lang.ClassCastException
@ -161,7 +162,9 @@ type No_Such_Method
## PRIVATE ## PRIVATE
Convert the No_Such_Method error to a human-readable format. Convert the No_Such_Method error to a human-readable format.
to_display_text : Text to_display_text : Text
to_display_text self = "Method `"+self.method_name+"` of "+self.target.to_display_text+" could not be found." to_display_text self =
target_type_name = if Meta.is_polyglot self.target then self.target.to_display_text else (Meta.type_of self.target).to_display_text
"Method `"+self.method_name+"` of type "+target_type_name+" could not be found."
@Builtin_Type @Builtin_Type
type No_Such_Field type No_Such_Field
@ -174,6 +177,12 @@ type No_Such_Field
- field_name: The name of the field that was being accessed. - field_name: The name of the field that was being accessed.
Error value field_name Error value field_name
## PRIVATE
Convert the No_Such_Method error to a human-readable format.
to_display_text : Text
to_display_text self =
value_type_name = if Meta.is_polyglot self.value then self.value.to_display_text else (Meta.type_of self.value).to_display_text
"Field `"+self.field_name+"` of "+value_type_name+" could not be found."
@Builtin_Type @Builtin_Type
type Module_Not_In_Package_Error type Module_Not_In_Package_Error
@ -214,7 +223,7 @@ type Incomparable_Values
to_display_text : Text to_display_text : Text
to_display_text self = to_display_text self =
case self.left.is_nothing && self.right.is_nothing of case self.left.is_nothing && self.right.is_nothing of
True -> "Incomparable_Values.Error" True -> "Incomparable_Values"
False -> "Cannot compare `" + self.left.to_text + "` with `" + self.right.to_text + "`" False -> "Cannot compare `" + self.left.to_text + "` with `" + self.right.to_text + "`"
## PRIVATE ## PRIVATE

View File

@ -12,7 +12,6 @@ type Project_Description
- prim_config: The primitive config of the project. - prim_config: The primitive config of the project.
Value prim_root_file prim_config Value prim_root_file prim_config
## Returns the root directory of the project. ## Returns the root directory of the project.
> Example > Example

View File

@ -1,4 +1,5 @@
import project.Data.Boolean.Boolean import project.Data.Boolean.Boolean
import project.Data.Text.Text
type HTTP_Status_Code type HTTP_Status_Code
## 100 Continue. ## 100 Continue.
@ -170,3 +171,49 @@ type HTTP_Status_Code
## Does the status code represent a successful response? ## Does the status code represent a successful response?
is_success : Boolean is_success : Boolean
is_success self = self.code >= 200 && self.code < 300 is_success self = self.code >= 200 && self.code < 300
## PRIVATE
Convert to a display representation of this HTTP_Status_Code.
to_display_text : Text
to_display_text self = case self.code of
100 -> "Continue"
101 -> "Switching Protocols"
200 -> "OK"
201 -> "Created"
202 -> "Accepted"
203 -> "Non-Authoritative Information"
204 -> "No Content"
205 -> "Reset Content"
206 -> "Partial Content"
300 -> "Multiple Choices"
301 -> "Moved Permanently"
302 -> "Found"
303 -> "See Other"
304 -> "Not Modified"
305 -> "Use Proxy"
307 -> "Temporary Redirect"
400 -> "Bad Request"
401 -> "Unauthorized"
402 -> "Payment Required"
403 -> "Forbidden"
404 -> "Not Found"
405 -> "Method Not Allowed"
406 -> "Not Acceptable"
407 -> "Proxy Authentication Required"
408 -> "Request Timeout"
409 -> "Conflict"
410 -> "Gone"
411 -> "Length Required"
412 -> "Precondition Failed"
413 -> "Request Entity Too Large"
414 -> "Request-URI Too Long"
415 -> "Unsupported Media Type"
416 -> "Requested Range Not Satisfiable"
417 -> "Expectation Failed"
500 -> "Internal Server Error"
501 -> "Not Implemented"
502 -> "Bad Gateway"
503 -> "Service Unavailable"
504 -> "Gateway Timeout"
505 -> "HTTP Version Not Supported"
_ -> "HTTP Status Code: " + self.code.to_text

View File

@ -173,6 +173,12 @@ type Header
text_plain : Header text_plain : Header
text_plain = Header.content_type "text/plain" text_plain = Header.content_type "text/plain"
## PRIVATE
Convert to a display representation of this Header.
to_display_text : Text
to_display_text self = self.name + ": " + self.value.to_display_text
## PRIVATE ## PRIVATE
type Header_Comparator type Header_Comparator
## PRIVATE ## PRIVATE

View File

@ -188,6 +188,11 @@ type URI
to_text : Text to_text : Text
to_text self = self.internal_uri.toString to_text self = self.internal_uri.toString
## PRIVATE
Convert to a display representation of this URI.
to_display_text : Text
to_display_text self = self.to_text.to_display_text
## PRIVATE ## PRIVATE
Convert to a JavaScript Object representing this URI. Convert to a JavaScript Object representing this URI.

View File

@ -3,6 +3,8 @@ import project.Data.Boolean.Boolean
import project.Data.Numbers.Integer import project.Data.Numbers.Integer
import project.Data.Numbers.Decimal import project.Data.Numbers.Decimal
import project.Data.Vector.Vector import project.Data.Vector.Vector
import project.Data.Json.JS_Object
import project.Data.Text.Text
import project.Error.Error import project.Error.Error
import project.Errors.Illegal_Argument.Illegal_Argument import project.Errors.Illegal_Argument.Illegal_Argument
import project.System import project.System
@ -53,6 +55,17 @@ type Random_Number_Generator
if range < Java_Integer.MAX_VALUE then min + (self.java_random.nextInt range) else if range < Java_Integer.MAX_VALUE then min + (self.java_random.nextInt range) else
Error.throw (Illegal_Argument.Error "Currently only integer ranges of up to 2^31-1 are supported.") Error.throw (Illegal_Argument.Error "Currently only integer ranges of up to 2^31-1 are supported.")
## PRIVATE
Serialise to JS_Object
to_js_object : JS_Object
to_js_object self =
JS_Object.from_pairs [["type", "Random_Number_Generator"], ["constructor", "new"]]
## PRIVATE
Convert to a display representation of this Random_Number_Generator.
to_display_text : Text
to_display_text self = "Random_Number_Generator"
## Returns a new vector containing a random sample of the input vector, without ## Returns a new vector containing a random sample of the input vector, without
replacement. replacement.

View File

@ -676,6 +676,11 @@ type File
to_text : Text to_text : Text
to_text self = self.absolute . path to_text self = self.absolute . path
## PRIVATE
Convert to a display representation of this File.
to_display_text : Text
to_display_text self = self.to_text
## PRIVATE ## PRIVATE
An output stream, allowing for interactive writing of contents into an An output stream, allowing for interactive writing of contents into an
open file. open file.

View File

@ -1,5 +1,6 @@
import project.Data.Vector.Vector
import project.Data.Boolean.Boolean import project.Data.Boolean.Boolean
import project.Data.Text.Text
import project.Data.Vector.Vector
polyglot java import java.nio.file.attribute.PosixFilePermission polyglot java import java.nio.file.attribute.PosixFilePermission
@ -108,3 +109,12 @@ type File_Permissions
others.append Permission.Execute others.append Permission.Execute
File_Permissions.Value owner.to_vector group.to_vector others.to_vector File_Permissions.Value owner.to_vector group.to_vector others.to_vector
## PRIVATE
Convert to a display representation of this File_Permissions.
to_display_text : Text
to_display_text self =
owner = "Owner: " + (if self.owner_read then "r" else "-") + (if self.owner_write then "w" else "-") + (if self.owner_execute then "x" else "-")
group = "Group: " + (if self.group_read then "r" else "-") + (if self.group_write then "w" else "-") + (if self.group_execute then "x" else "-")
other = "Other: " + (if self.others_read then "r" else "-") + (if self.others_write then "w" else "-") + (if self.others_execute then "x" else "-")
owner + ", " + group + ", " + other

View File

@ -1,4 +1,5 @@
import project.Data.Numbers.Integer import project.Data.Numbers.Integer
import project.Data.Text.Text
## The exit codes that the process can return. ## The exit codes that the process can return.
type Exit_Code type Exit_Code
@ -39,3 +40,10 @@ type Exit_Code
to_number self = case self of to_number self = case self of
Exit_Code.Success -> 0 Exit_Code.Success -> 0
Exit_Code.Failure code -> code Exit_Code.Failure code -> code
## PRIVATE
Convert to a display representation of this Exit_Code.
to_display_text : Text
to_display_text self = case self of
Exit_Code.Success -> "Success"
Exit_Code.Failure code -> "Failure " + code.to_display_text

View File

@ -78,3 +78,10 @@ type Process_Builder
type Process_Result type Process_Result
## PRIVATE ## PRIVATE
Value exit_code:Exit_Code stdout:Text stderr:Text Value exit_code:Exit_Code stdout:Text stderr:Text
## PRIVATE
Convert to a display representation of this Process_Result.
to_display_text : Text
to_display_text self = case self.exit_code of
Exit_Code.Success -> "Success(" + self.stdout.to_display_text + ")"
_ -> self.exit_code.to_display_text + "(" + self.stderr.to_display_text + ")"

View File

@ -28,7 +28,7 @@ type Warning
## ADVANCED ## ADVANCED
Are any warnings attached to the value? Are any warnings attached to the value?
has_warnings : Any -> Boolean has_warnings : Any -> Any -> Boolean
has_warnings value warning_type=Any = has_warnings value warning_type=Any =
Warning.get_all value . any (w-> w.value.is_a warning_type) Warning.get_all value . any (w-> w.value.is_a warning_type)
@ -36,7 +36,7 @@ type Warning
Arguments: Arguments:
- warning_type: The type to remove if attached to the value. Defaults to all warnings. - warning_type: The type to remove if attached to the value. Defaults to all warnings.
remove_warnings : Any -> Any remove_warnings : Any -> Any -> Any
remove_warnings value warning_type=Any = remove_warnings value warning_type=Any =
Warning.detach_selected_warnings value (w-> w.is_a warning_type) . first Warning.detach_selected_warnings value (w-> w.is_a warning_type) . first

View File

@ -1497,6 +1497,7 @@ type Table
suffixing strategy. suffixing strategy.
parse_to_columns : Text | Integer -> Text -> Case_Sensitivity -> Boolean -> Problem_Behavior -> Table parse_to_columns : Text | Integer -> Text -> Case_Sensitivity -> Boolean -> Problem_Behavior -> Table
parse_to_columns self column pattern="." case_sensitivity=Case_Sensitivity.Sensitive parse_values=True on_problems=Report_Error = parse_to_columns self column pattern="." case_sensitivity=Case_Sensitivity.Sensitive parse_values=True on_problems=Report_Error =
_ = [column, pattern, case_sensitivity, parse_values, on_problems]
Error.throw (Unsupported_Database_Operation.Error "Table.parse_to_columns is not implemented yet for the Database backends.") Error.throw (Unsupported_Database_Operation.Error "Table.parse_to_columns is not implemented yet for the Database backends.")
## PRIVATE ## PRIVATE

View File

@ -300,7 +300,6 @@ type Value_Type
to_js_object : JS_Object to_js_object : JS_Object
to_js_object self = to_js_object self =
constructor_name = Meta.meta self . constructor . name constructor_name = Meta.meta self . constructor . name
display_text = self.to_display_text
additional_fields = case self of additional_fields = case self of
Value_Type.Integer size -> Value_Type.Integer size ->
[["bits", size.to_bits]] [["bits", size.to_bits]]
@ -316,7 +315,7 @@ type Value_Type
[["type_name", type_name]] [["type_name", type_name]]
_ -> [] _ -> []
JS_Object.from_pairs <| JS_Object.from_pairs <|
[["type", "Value_Type"], ["constructor", constructor_name], ["_display_text_", display_text]] + additional_fields [["type", "Value_Type"], ["constructor", constructor_name]] + additional_fields
## The type representing inferring the column type automatically based on values ## The type representing inferring the column type automatically based on values
present in the column. present in the column.

View File

@ -50,7 +50,7 @@ type Too_Many_Column_Names_Provided
## One or more column names were invalid during a rename operation. ## One or more column names were invalid during a rename operation.
type Invalid_Output_Column_Names type Invalid_Output_Column_Names
Error (column_names : [Text]) Error (column_names : Vector Text)
## PRIVATE ## PRIVATE

View File

@ -1,4 +1,5 @@
from Standard.Base import all from Standard.Base import all
import Standard.Base.Data.Vector.Builder
import Standard.Table.Data.Table.Table as Dataframe_Table import Standard.Table.Data.Table.Table as Dataframe_Table
import Standard.Table.Data.Column.Column as Dataframe_Column import Standard.Table.Data.Column.Column as Dataframe_Column
@ -21,46 +22,109 @@ import project.Helpers
prepare_visualization : Any -> Integer -> Text prepare_visualization : Any -> Integer -> Text
prepare_visualization y max_rows=1000 = Helpers.recover_errors <| prepare_visualization y max_rows=1000 = Helpers.recover_errors <|
x = Warning.set y [] x = Warning.set y []
case x of
result = case x of
_ : Vector -> make_json_for_vector x max_rows
_ : Array -> prepare_visualization x.to_vector max_rows
_ : Map -> make_json_for_map x max_rows
_ : JS_Object -> make_json_for_js_object x max_rows
_ : Dataframe_Column -> prepare_visualization x.to_table max_rows
_ : Dataframe_Table -> _ : Dataframe_Table ->
dataframe = x.take (First max_rows) dataframe = x.take max_rows
all_rows_count = x.row_count all_rows_count = x.row_count
included_rows = dataframe.row_count index = Dataframe_Column.from_vector "#" (Vector.new dataframe.row_count i->i)
index = Dataframe_Column.from_vector "#" (Vector.new included_rows i->i) make_json_for_table dataframe [index] all_rows_count
_ : Database_Column -> prepare_visualization x.to_table max_rows
make_json dataframe [index] all_rows_count
_ : Database_Table -> _ : Database_Table ->
df = x.read max_rows dataframe = x.read max_rows
all_rows_count = x.row_count all_rows_count = x.row_count
make_json_for_table dataframe [] all_rows_count
make_json df [] all_rows_count
# We display columns as 1-column tables.
_ : Dataframe_Column ->
prepare_visualization x.to_table max_rows
_ : Database_Column ->
prepare_visualization x.to_table max_rows
# TODO [RW] Should we truncate Vectors?
# We also visualize Vectors and arrays
_ : Vector ->
truncated = x.take (First max_rows)
JS_Object.from_pairs [["json", truncated], ["all_rows_count", x.length]] . to_text
_ : Array ->
prepare_visualization (Vector.from_polyglot_array x) max_rows
# Serialize Maps
_ : Map ->
map_vector = Warning.clear (x.to_vector.take max_rows)
header = ["header", ["key", "value"]]
data = ["data", [map_vector.map .first, map_vector.map .second]]
all_rows = ["all_rows_count", x.size]
JS_Object.from_pairs [header, data, all_rows] . to_text
# Anything else will be visualized with the JSON or matrix visualization
_ -> _ ->
JS_Object.from_pairs [["json", x]] . to_text js_value = x.to_js_object
value = if js_value.is_a JS_Object . not then js_value else
pairs = [['_display_text_', x.to_display_text]] + js_value.field_names.map f-> [f, make_json_for_value (js_value.get f)]
JS_Object.from_pairs pairs
JS_Object.from_pairs [["json", value]]
result.to_text
## Column Limit
max_columns = 250
## PRIVATE
Render Vector to JSON
make_json_for_vector : Vector -> Integer -> JS_Object
make_json_for_vector vector max_rows =
all_rows = ["all_rows_count", vector.length]
truncated = vector.take max_rows
matrix = make_json_for_matrix (Vector.new_builder truncated.length) truncated
if matrix.is_nothing.not then JS_Object.from_pairs [["type", "Matrix"], all_rows, ["json", matrix], ["column_count", matrix.fold 0 c->v-> if v.is_nothing then c else c.max v.length]] else
object_matrix = make_json_for_object_matrix (Vector.new_builder truncated.length) truncated
if object_matrix.is_nothing.not then JS_Object.from_pairs [["type", "Object_Matrix"], all_rows, ["json", object_matrix]] else
JS_Object.from_pairs [["type", "Vector"], all_rows, ["json", truncated.map make_json_for_value]]
## PRIVATE
Render Vector of Vector / Array to JSON
make_json_for_matrix : Builder -> Vector -> Integer -> Vector | Nothing
make_json_for_matrix current vector idx=0 = if idx == vector.length then current.to_vector else
row = vector.at idx
to_append = case row of
Nothing -> Nothing
_ : Vector -> row.take max_columns . map make_json_for_value
_ : Array -> row.to_vector.take max_columns . map make_json_for_value
_ -> False
if to_append == False then Nothing else
next = current.append to_append
@Tail_Call make_json_for_matrix next vector idx+1
## PRIVATE
Render Vector of Objects to JSON
make_json_for_object_matrix : Builder -> Vector -> Integer -> Vector | Nothing
make_json_for_object_matrix current vector idx=0 = if idx == vector.length then current.to_vector else
row = vector.at idx
to_append = case row of
Nothing -> Nothing
_ : Date -> False
_ : Time_Of_Day -> False
_ : Date_Time -> False
_ : Duration -> False
_ : Period -> False
_ : Map ->
pairs = row.keys.map k-> [k.to_text, make_json_for_value (row.get k)]
JS_Object.from_pairs pairs
_ ->
js_object = row.to_js_object
if js_object.is_a JS_Object . not then False else
if js_object.field_names.sort == ["type" , "constructor"] then False else
pairs = js_object.field_names.map f-> [f, make_json_for_value (js_object.get f)]
JS_Object.from_pairs pairs
if to_append == False then Nothing else
next = current.append to_append
@Tail_Call make_json_for_object_matrix next vector idx+1
## PRIVATE
Render Map to JSON
make_json_for_map : Map -> Integer -> JS_Object
make_json_for_map map max_items =
header = ["header", ["key", "value"]]
all_rows = ["all_rows_count", map.size]
map_vector = Warning.clear (map.to_vector.take max_items)
mapped = map_vector . map p-> [p.first.to_text, make_json_for_value p.second]
data = ["data", [mapped.map .first, mapped.map .second]]
JS_Object.from_pairs [header, data, all_rows, ["type", "Map"]]
## PRIVATE
Render JS_Object to JSON
make_json_for_js_object : JS_Object -> Integer -> JS_Object
make_json_for_js_object js_object max_items =
fields = js_object.field_names
header = ["header", ["key", "value"]]
all_rows = ["all_rows_count", fields.length]
map_vector = Warning.clear (fields.take max_items)
mapped = map_vector . map p-> [p, make_json_for_value (js_object.get p)]
data = ["data", [mapped.map .first, mapped.map .second]]
JS_Object.from_pairs [header, data, all_rows, ["type", "Map"]]
## PRIVATE ## PRIVATE
Creates a JSON representation for the visualizations. Creates a JSON representation for the visualizations.
@ -73,14 +137,51 @@ prepare_visualization y max_rows=1000 = Helpers.recover_errors <|
`dataframe`. `dataframe`.
- all_rows_count: the number of all rows in the underlying data, useful if - all_rows_count: the number of all rows in the underlying data, useful if
only a fragment is displayed. only a fragment is displayed.
make_json : (Dataframe_Table | Database_Table) -> Vector Dataframe_Column -> Integer -> Text make_json_for_table : Dataframe_Table -> Vector Dataframe_Column -> Integer -> JS_Object
make_json dataframe indices all_rows_count = make_json_for_table dataframe indices all_rows_count =
get_vector c = Warning.set c.to_vector [] get_vector c = Warning.set (c.to_vector.map v-> make_json_for_value v) []
columns = dataframe.columns columns = dataframe.columns
header = ["header", columns.map .name] header = ["header", columns.map .name]
data = ["data", columns.map get_vector] data = ["data", columns.map get_vector]
all_rows = ["all_rows_count", all_rows_count] all_rows = ["all_rows_count", all_rows_count]
ixes = ["indices", indices.map get_vector] ixes = ["indices", indices.map get_vector]
ixes_header = ["indices_header", indices.map .name] ixes_header = ["indices_header", indices.map .name]
pairs = [header, data, all_rows, ixes, ixes_header] pairs = [header, data, all_rows, ixes, ixes_header, ["type", "Table"]]
JS_Object.from_pairs pairs . to_text JS_Object.from_pairs pairs
## PRIVATE
Create JSON serialization of values for the table.
make_json_for_value : Any -> Integer -> Text
make_json_for_value val level=0 = case val of
Nothing -> Nothing
_ : Text -> val
_ : Number ->
js_version = val.to_js_object
if js_version.is_a JS_Object . not then js_version else
pairs = [['_display_text_', val.to_display_text]] + js_version.field_names.map f-> [f, js_version.get f]
JS_Object.from_pairs pairs
_ : Boolean -> val
_ : Vector ->
if level != 0 then "[… "+val.length.to_text+" items]" else
truncated = val.take 5 . map v-> (make_json_for_value v level+1).to_text
prepared = if val.length > 5 then truncated + ["… " + (val.length - 5).to_text+ " items"] else truncated
"[" + (prepared.join ", ") + "]"
_ : Array -> make_json_for_value val.to_vector level
_ : Map ->
if level != 0 then "{… "+val.size.to_text+" items}" else
truncated = val.keys.take 5 . map k-> k.to_text + ": " + (make_json_for_value (val.get k) level+1).to_text
prepared = if val.length > 5 then truncated + ["… " + (val.length - 5).to_text+ " items"] else truncated
"{" + (prepared.join ", ") + "}"
_ : Dataframe_Column -> make_json_for_value val.to_table level
_ : Database_Column -> make_json_for_value val.to_table level
_ : Dataframe_Table ->
if level != 0 then "Table{" + val.row_count + " rows x " + val.column_count + " columns}" else
truncated = val.columns.take 5 . map _.name
prepared = if val.column_count > 5 then truncated + ["… " + (val.column_count - 5).to_text+ " more"] else truncated
"Table{" + val.row_count.to_text + " rows x [" + (prepared.join ", ") + "]}"
_ : Database_Table ->
if level != 0 then "Table{" + val.row_count + " rows x " + val.column_count + " columns}" else
truncated = val.columns.take 5 . map _.name
prepared = if val.column_count > 5 then truncated + ["… " + (val.column_count - 5).to_text+ " more"] else truncated
"Table{" + val.row_count.to_text + " rows x [" + (prepared.join ", ") + "]}"
_ -> val.to_display_text

View File

@ -2989,7 +2989,7 @@ class RuntimeServerTest
contextId, contextId,
Seq( Seq(
Api.ExecutionResult.Diagnostic.error( Api.ExecutionResult.Diagnostic.error(
"Type error: expected a function, but got 42 (Integer).", "Type error: expected a function, but got 42.",
Some(mainFile), Some(mainFile),
Some(model.Range(model.Position(1, 7), model.Position(1, 19))), Some(model.Range(model.Position(1, 7), model.Position(1, 19))),
None, None,
@ -3136,7 +3136,7 @@ class RuntimeServerTest
contextId, contextId,
Seq( Seq(
Api.ExecutionResult.Diagnostic.error( Api.ExecutionResult.Diagnostic.error(
"Method `+` of x (Unresolved_Symbol) could not be found.", "Method `+` of type Function could not be found.",
Some(mainFile), Some(mainFile),
Some(model.Range(model.Position(3, 14), model.Position(3, 23))), Some(model.Range(model.Position(3, 14), model.Position(3, 23))),
None, None,
@ -3440,7 +3440,7 @@ class RuntimeServerTest
contextId, contextId,
Seq( Seq(
Api.ExecutionResult.Diagnostic.error( Api.ExecutionResult.Diagnostic.error(
"Method `pi` of Number could not be found.", "Method `pi` of type Number.type could not be found.",
Some(mainFile), Some(mainFile),
Some(model.Range(model.Position(3, 7), model.Position(3, 16))), Some(model.Range(model.Position(3, 7), model.Position(3, 16))),
None, None,

View File

@ -1733,10 +1733,11 @@ class RuntimeVisualizationsTest
Api.Response( Api.Response(
requestId, requestId,
Api.VisualisationExpressionFailed( Api.VisualisationExpressionFailed(
"Method `does_not_exist` of Main could not be found.", "Method `does_not_exist` of type Main could not be found.",
Some( Some(
Api.ExecutionResult.Diagnostic.error( Api.ExecutionResult.Diagnostic.error(
message = "Method `does_not_exist` of Main could not be found.", message =
"Method `does_not_exist` of type Main could not be found.",
stack = Vector( stack = Vector(
Api.StackTraceElement("<eval>", None, None, None), Api.StackTraceElement("<eval>", None, None, None),
Api.StackTraceElement("Debug.eval", None, None, None) Api.StackTraceElement("Debug.eval", None, None, None)
@ -1817,10 +1818,10 @@ class RuntimeVisualizationsTest
contextId, contextId,
visualisationId, visualisationId,
idMain, idMain,
"Method `visualise_me` of 50 (Integer) could not be found.", "Method `visualise_me` of type Integer could not be found.",
Some( Some(
Api.ExecutionResult.Diagnostic.error( Api.ExecutionResult.Diagnostic.error(
"Method `visualise_me` of 50 (Integer) could not be found.", "Method `visualise_me` of type Integer could not be found.",
None, None,
Some(model.Range(model.Position(0, 5), model.Position(0, 19))), Some(model.Range(model.Position(0, 5), model.Position(0, 19))),
None, None,
@ -1929,10 +1930,10 @@ class RuntimeVisualizationsTest
contextId, contextId,
visualisationId, visualisationId,
idMain, idMain,
"Method `visualise_me` of 51 (Integer) could not be found.", "Method `visualise_me` of type Integer could not be found.",
Some( Some(
Api.ExecutionResult.Diagnostic.error( Api.ExecutionResult.Diagnostic.error(
"Method `visualise_me` of 51 (Integer) could not be found.", "Method `visualise_me` of type Integer could not be found.",
Some(visualisationFile), Some(visualisationFile),
Some(model.Range(model.Position(1, 11), model.Position(1, 25))), Some(model.Range(model.Position(1, 11), model.Position(1, 25))),
None, None,
@ -2134,10 +2135,10 @@ class RuntimeVisualizationsTest
contextId, contextId,
visualisationId, visualisationId,
idMain, idMain,
"42 (Integer)", "42",
Some( Some(
Api.ExecutionResult.Diagnostic.error( Api.ExecutionResult.Diagnostic.error(
message = "42 (Integer)", message = "42",
file = Some(mainFile), file = Some(mainFile),
location = location =
Some(model.Range(model.Position(3, 4), model.Position(3, 18))), Some(model.Range(model.Position(3, 4), model.Position(3, 18))),

View File

@ -1,6 +1,5 @@
package org.enso.interpreter.node.expression.builtin.text; package org.enso.interpreter.node.expression.builtin.text;
import com.ibm.icu.text.BreakIterator;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Fallback;
@ -13,6 +12,7 @@ import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.text.util.TypeToDisplayTextNode; import org.enso.interpreter.node.expression.builtin.text.util.TypeToDisplayTextNode;
import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
import org.enso.polyglot.common_utils.Core_Text_Utils; import org.enso.polyglot.common_utils.Core_Text_Utils;
@BuiltinMethod(type = "Any", name = "to_display_text") @BuiltinMethod(type = "Any", name = "to_display_text")
@ -35,6 +35,23 @@ public abstract class AnyToDisplayTextNode extends Node {
} }
} }
@Specialization
@CompilerDirectives.TruffleBoundary
Text convertInteger(long self) {
return Text.create(Long.toString(self));
}
@Specialization
@CompilerDirectives.TruffleBoundary
Text convertDouble(double self) {
return Text.create(Double.toString(self));
}
@Specialization
Text convertBigInteger(EnsoBigInteger bigInteger) {
return Text.create(bigInteger.toString());
}
@Specialization @Specialization
Text convertText(Text self) { Text convertText(Text self) {
final var limit = 80; final var limit = 80;
@ -47,8 +64,8 @@ public abstract class AnyToDisplayTextNode extends Node {
@CompilerDirectives.TruffleBoundary @CompilerDirectives.TruffleBoundary
private static Text takePrefix(Text self, final int limit) { private static Text takePrefix(Text self, final int limit) {
var prefix = Core_Text_Utils.take_prefix(self.toString(), limit); var prefix = Core_Text_Utils.take_prefix(self.toString(), limit - 2);
return Text.create(prefix); return Text.create(prefix + "");
} }
@Fallback @Fallback

View File

@ -118,7 +118,7 @@ public class TypeMembersTest {
v.invokeMember(k); v.invokeMember(k);
fail("Invoking " + k + " on " + v + " shall fail"); fail("Invoking " + k + " on " + v + " shall fail");
} catch (PolyglotException ex) { } catch (PolyglotException ex) {
assertEquals("No_Such_Field.Error", ex.getMessage()); assertEquals("Field `" + k + "` of IntList could not be found.", ex.getMessage());
} }
} else { } else {
assertNotNull(msg + " - can be invoked", v.invokeMember(k)); assertNotNull(msg + " - can be invoked", v.invokeMember(k));

View File

@ -14,7 +14,7 @@ class ImportsTest extends PackageTest {
"Overloaded methods" should "not be visible when not imported" in { "Overloaded methods" should "not be visible when not imported" in {
the[InterpreterException] thrownBy evalTestProject( the[InterpreterException] thrownBy evalTestProject(
"TestNonImportedOverloads" "TestNonImportedOverloads"
) should have message "Method `method` of Mk_X could not be found." ) should have message "Method `method` of type X could not be found."
} }
"Import statements" should "report errors when they cannot be resolved" in { "Import statements" should "report errors when they cannot be resolved" in {
@ -101,13 +101,13 @@ class ImportsTest extends PackageTest {
"Importing module's types" should "not bring extension methods into the scope " in { "Importing module's types" should "not bring extension methods into the scope " in {
the[InterpreterException] thrownBy evalTestProject( the[InterpreterException] thrownBy evalTestProject(
"Test_Extension_Methods_Failure" "Test_Extension_Methods_Failure"
) should have message "Method `foo` of 1 (Integer) could not be found." ) should have message "Method `foo` of type Integer could not be found."
} }
"Compiler" should "detect name conflicts preventing users from importing submodules" in { "Compiler" should "detect name conflicts preventing users from importing submodules" in {
the[InterpreterException] thrownBy evalTestProject( the[InterpreterException] thrownBy evalTestProject(
"TestSubmodulesNameConflict" "TestSubmodulesNameConflict"
) should have message "Method `c_mod_method` of C could not be found." ) should have message "Method `c_mod_method` of type C.type could not be found."
val outLines = consumeOut val outLines = consumeOut
outLines(2) should include outLines(2) should include
"Declaration of type C shadows module local.TestSubmodulesNameConflict.A.B.C making it inaccessible via a qualified name." "Declaration of type C shadows module local.TestSubmodulesNameConflict.A.B.C making it inaccessible via a qualified name."
@ -183,7 +183,7 @@ class ImportsTest extends PackageTest {
"Fully qualified names" should "detect conflicts with the exported types sharing the namespace" in { "Fully qualified names" should "detect conflicts with the exported types sharing the namespace" in {
the[InterpreterException] thrownBy evalTestProject( the[InterpreterException] thrownBy evalTestProject(
"Test_Fully_Qualified_Name_Conflict" "Test_Fully_Qualified_Name_Conflict"
) should have message "Method `Foo` of Atom could not be found." ) should have message "Method `Foo` of type Atom.type could not be found."
val outLines = consumeOut val outLines = consumeOut
outLines should have length 3 outLines should have length 3
outLines( outLines(

View File

@ -107,7 +107,7 @@ class MethodsTest extends InterpreterTest {
|""".stripMargin |""".stripMargin
the[InterpreterException] thrownBy eval( the[InterpreterException] thrownBy eval(
code code
) should have message "Method `foo` of 7 (Integer) could not be found." ) should have message "Method `foo` of type Integer could not be found."
} }
"be callable for any type when defined on Any" in { "be callable for any type when defined on Any" in {
@ -216,7 +216,7 @@ class MethodsTest extends InterpreterTest {
|""".stripMargin |""".stripMargin
the[InterpreterException] thrownBy eval( the[InterpreterException] thrownBy eval(
code code
) should have message "Method `new` of Mk_Foo could not be found." ) should have message "Method `new` of type Foo could not be found."
} }
"not be callable on Nothing when non-static" in { "not be callable on Nothing when non-static" in {

View File

@ -131,10 +131,10 @@ class TextTest extends InterpreterTest {
"Syntax error: foo.", "Syntax error: foo.",
"Type error: expected `myvar` to be Nothing, but got List.", "Type error: expected `myvar` to be Nothing, but got List.",
"Compile error: error :(.", "Compile error: error :(.",
"Inexhaustive pattern match: no branch matches 32 (Integer).", "Inexhaustive pattern match: no branch matches 32.",
"Arithmetic error: cannot frobnicate quaternions.", "Arithmetic error: cannot frobnicate quaternions.",
"Type error: expected `that` to be Number, but got Text.", "Type error: expected `that` to be Number, but got Text.",
"Type error: expected a function, but got 7 (Integer).", "Type error: expected a function, but got 7.",
"Wrong number of arguments. Expected 10, but got 20." "Wrong number of arguments. Expected 10, but got 20."
) )
} }

View File

@ -413,6 +413,7 @@ spec =
pUS2.to_vector . should_equal [1, 123, -1234, 1234567, Nothing] pUS2.to_vector . should_equal [1, 123, -1234, 1234567, Nothing]
Problems.expect_warning (Invalid_Format.Error "ints" Value_Type.Integer ["12,34,56"]) pUS2 Problems.expect_warning (Invalid_Format.Error "ints" Value_Type.Integer ["12,34,56"]) pUS2
## Reject minus sign if already seen a bracket form
cUS3 = Column.from_vector "ints" ["1", "(000,123)", "-1,234", "1,234,567", "12,34,56"] cUS3 = Column.from_vector "ints" ["1", "(000,123)", "-1,234", "1,234,567", "12,34,56"]
pUS3 = cUS3.parse type=Value_Type.Integer pUS3 = cUS3.parse type=Value_Type.Integer
pUS3.to_vector . should_equal [1, -123, Nothing, 1234567, Nothing] pUS3.to_vector . should_equal [1, -123, Nothing, 1234567, Nothing]
@ -423,6 +424,12 @@ spec =
pUS4.to_vector . should_equal [1234, 1234, 1234567, -1234, Nothing] pUS4.to_vector . should_equal [1234, 1234, 1234567, -1234, Nothing]
Problems.expect_warning (Invalid_Format.Error "ints" Value_Type.Integer ["($1,234,567)"]) pUS4 Problems.expect_warning (Invalid_Format.Error "ints" Value_Type.Integer ["($1,234,567)"]) pUS4
## Reject bracket notation for negative numbers if already seen a minus sign
cUS5 = Column.from_vector "ints" ["1", "000123", "-1,234", "1,234,567", "(123,456)"]
pUS5 = cUS5.parse type=Value_Type.Integer
pUS5.to_vector . should_equal [1, 123, -1234, 1234567, Nothing]
Problems.expect_warning (Invalid_Format.Error "ints" Value_Type.Integer ["(123,456)"]) pUS5
Test.specify "should correctly parse integers in European formats" <| Test.specify "should correctly parse integers in European formats" <|
cDE = Column.from_vector "ints" ["1", "000123", "-1.234", "1.234.567", "12.34.56"] cDE = Column.from_vector "ints" ["1", "000123", "-1.234", "1.234.567", "12.34.56"]
pDE = cDE.parse type=Value_Type.Integer pDE = cDE.parse type=Value_Type.Integer
@ -470,6 +477,7 @@ spec =
pUS2.to_vector . should_equal [1.23, 123, -1234.567, 1234567.789, Nothing] pUS2.to_vector . should_equal [1.23, 123, -1234.567, 1234567.789, Nothing]
Problems.expect_warning (Invalid_Format.Error "floats" Value_Type.Float ["12,34.56"]) pUS2 Problems.expect_warning (Invalid_Format.Error "floats" Value_Type.Float ["12,34.56"]) pUS2
## Reject minus sign if already seen a bracket form
cUS3 = Column.from_vector "floats" ["1.23", "(000,123)", "-1,234.567", "1,234,567.789", "12,34.56"] cUS3 = Column.from_vector "floats" ["1.23", "(000,123)", "-1,234.567", "1,234,567.789", "12,34.56"]
pUS3 = cUS3.parse type=Value_Type.Float pUS3 = cUS3.parse type=Value_Type.Float
pUS3.to_vector . should_equal [1.23, -123, Nothing, 1234567.789, Nothing] pUS3.to_vector . should_equal [1.23, -123, Nothing, 1234567.789, Nothing]
@ -480,6 +488,12 @@ spec =
pUS4.to_vector . should_equal [12.34, 1234, 1234567.789, -1234.96, Nothing] pUS4.to_vector . should_equal [12.34, 1234, 1234567.789, -1234.96, Nothing]
Problems.expect_warning (Invalid_Format.Error "floats" Value_Type.Float ["($1,234,567)"]) pUS4 Problems.expect_warning (Invalid_Format.Error "floats" Value_Type.Float ["($1,234,567)"]) pUS4
## Reject bracket notation for negative numbers if already seen a minus sign
cUS5 = Column.from_vector "floats" ["1.23", "000123", "-1,234.567", "1,234,567.789", "(123,456)"]
pUS5 = cUS5.parse type=Value_Type.Float
pUS5.to_vector . should_equal [1.23, 123, -1234.567, 1234567.789, Nothing]
Problems.expect_warning (Invalid_Format.Error "floats" Value_Type.Float ["(123,456)"]) pUS5
Test.specify "should correctly parse decimals in European formats" <| Test.specify "should correctly parse decimals in European formats" <|
cDE = Column.from_vector "floats" ["1,23", "000123", "-1.234,567", "1.234.567,789", "12.34,56"] cDE = Column.from_vector "floats" ["1,23", "000123", "-1.234,567", "1.234.567,789", "12.34,56"]
pDE = cDE.parse type=Value_Type.Float pDE = cDE.parse type=Value_Type.Float

View File

@ -525,12 +525,14 @@ spec =
Test.specify "should work happily with mixed types" <| Test.specify "should work happily with mixed types" <|
c_0 = ['A', ["H", "B", "C"]] c_0 = ['A', ["H", "B", "C"]]
c_00 = ['AA', ["", "B", "C"]]
c_1 = ['B', [Date.new 1980, Date.new 1979, Date.new 2000]] c_1 = ['B', [Date.new 1980, Date.new 1979, Date.new 2000]]
c_2 = ['x', [1, 2, 3]] c_2 = ['x', [1, 2, 3]]
c_3 = ['Y', [5.3, 56.2, 6.3]] c_3 = ['Y', [5.3, 56.2, 6.3]]
c_4 = ['Z', [True, False, True]] c_4 = ['Z', [True, False, True]]
table = Table.new [c_0, c_1, c_2, c_3, c_4] c_5 = ['ZZ', [Nothing, False, True]]
expect_column_names ["H", "1980-01-01", "1", "5.3", "True"] <| table.use_first_row_as_names table = Table.new [c_0, c_00, c_1, c_2, c_3, c_4, c_5]
expect_column_names ["H", "Column_1", "1980-01-01", "1", "5.3", "True", "Column_2"] table.use_first_row_as_names
Test.specify "should correctly handle problems: invalid names ''" <| Test.specify "should correctly handle problems: invalid names ''" <|
c_0 = ['A', ["", "B", "C"]] c_0 = ['A', ["", "B", "C"]]

View File

@ -250,7 +250,7 @@ spec =
vec = [[0, 0], [3, -5], [1, 2], [0, 1]] vec = [[0, 0], [3, -5], [1, 2], [0, 1]]
m1 = Map.from_vector vec m1 = Map.from_vector vec
m1.should_fail_with Illegal_Argument m1.should_fail_with Illegal_Argument
m1.catch.message . should_equal "`Map.from_vector` encountered duplicate key: 0 (Integer)" m1.catch.message . should_equal "`Map.from_vector` encountered duplicate key: 0"
m2 = Map.from_vector vec allow_duplicates=True m2 = Map.from_vector vec allow_duplicates=True
Problems.assume_no_problems m2 Problems.assume_no_problems m2
@ -304,7 +304,7 @@ spec =
m = Map.from_vector [[1, 2], [11, 3]] m = Map.from_vector [[1, 2], [11, 3]]
m2 = m.transform (k -> v -> [k % 10, v*2]) m2 = m.transform (k -> v -> [k % 10, v*2])
m2.should_fail_with Illegal_Argument m2.should_fail_with Illegal_Argument
m2.catch.message . should_equal "`Map.transform` encountered duplicate key: 1 (Integer)" m2.catch.message . should_equal "`Map.transform` encountered duplicate key: 1"
Test.specify "should allow mapping over values" <| Test.specify "should allow mapping over values" <|
m = Map.empty . insert 1 2 . insert 2 4 m = Map.empty . insert 1 2 . insert 2 4

View File

@ -1,18 +1,12 @@
from Standard.Base import all from Standard.Base import all
import Standard.Base.Errors.Unimplemented.Unimplemented import Standard.Base.Errors.Unimplemented.Unimplemented
import Standard.Base.Data.Noise.Generator
import Standard.Base.Data.Noise.Deterministic_Random import Standard.Base.Data.Noise.Deterministic_Random
from Standard.Test import Test, Test_Suite from Standard.Test import Test, Test_Suite
import Standard.Test.Extensions import Standard.Test.Extensions
spec = spec =
Test.group "Generator Interface" <|
gen = Generator
Test.specify "should not be invokable" <|
interval = Interval.inclusive 0 1
Test.expect_panic_with (gen.step 1 interval) Unimplemented
Test.group "Deterministic Random Noise Generator" <| Test.group "Deterministic Random Noise Generator" <|
gen = Deterministic_Random gen = Deterministic_Random
Test.specify "should always return the same output for the same input" <| Test.specify "should always return the same output for the same input" <|

View File

@ -98,18 +98,18 @@ spec =
disp = long.to_display_text disp = long.to_display_text
disp.length . should_equal 80 disp.length . should_equal 80
disp.characters.take (First 5) . should_equal [ 'H', 'e', 'l', 'l', 'o' ] disp.characters.take (First 5) . should_equal [ 'H', 'e', 'l', 'l', 'o' ]
disp.characters.take (Last 6) . should_equal ['l', 'd', '!', ' ', 'H', 'e'] disp.characters.take (Last 6) . should_equal ['l', 'd', '!', ' ', ' ', '…']
Test.specify "grapheme 1 conversion" <| Test.specify "grapheme 1 conversion" <|
txt = 'a\u0321\u0302'*100 txt = 'a\u0321\u0302'*100
txt.to_display_text . should_equal 'a\u0321\u0302'*80 txt.to_display_text . should_equal ('a\u0321\u0302'*78 + ' …')
Test.specify "grapheme 2 conversion" <| Test.specify "grapheme 2 conversion" <|
txt = '\u0915\u094D\u0937\u093F'*100 txt = '\u0915\u094D\u0937\u093F'*100
txt.to_display_text . should_equal '\u0915\u094D\u0937\u093F'*80 txt.to_display_text . should_equal ('\u0915\u094D\u0937\u093F'*78 + ' …')
Test.specify "grapheme 3 conversion" <| Test.specify "grapheme 3 conversion" <|
txt = '\u{1F926}\u{1F3FC}\u200D\u2642\uFE0F'*100 txt = '\u{1F926}\u{1F3FC}\u200D\u2642\uFE0F'*100
txt.to_display_text . should_equal '\u{1F926}\u{1F3FC}\u200D\u2642\uFE0F'*80 txt.to_display_text . should_equal ('\u{1F926}\u{1F3FC}\u200D\u2642\uFE0F'*78 + ' …')
main = Test_Suite.run_main spec main = Test_Suite.run_main spec

View File

@ -142,6 +142,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
time . second . should_equal 1 time . second . should_equal 1
time . nanosecond . should_equal 0 time . nanosecond . should_equal 0
time . zone . zone_id . should_equal "Europe/Paris" time . zone . zone_id . should_equal "Europe/Paris"
time.to_display_text . should_equal "1970-01-01 00:00:01 Europe/Paris"
Test.specify "should throw error when parsing invalid time" <| Test.specify "should throw error when parsing invalid time" <|
case parse_datetime "2008-1-1" . catch of case parse_datetime "2008-1-1" . catch of
@ -199,6 +200,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
time . second . should_equal 1 time . second . should_equal 1
time . nanosecond . should_equal 0 time . nanosecond . should_equal 0
time . zone . zone_id . should_equal tz.zone_id time . zone . zone_id . should_equal tz.zone_id
time.to_display_text . should_equal "1970-01-01 01:01:01 +01:01:01"
Test.specify "should set id-based timezone" <| Test.specify "should set id-based timezone" <|
tz = Time_Zone.parse "Europe/Moscow" tz = Time_Zone.parse "Europe/Moscow"
@ -211,6 +213,7 @@ spec_with name create_new_datetime parse_datetime nanoseconds_loss_in_precision=
time . second . should_equal 0 time . second . should_equal 0
time . nanosecond . should_equal 0 time . nanosecond . should_equal 0
time . zone . zone_id . should_equal tz.zone_id time . zone . zone_id . should_equal tz.zone_id
time.to_display_text . should_equal "1970-01-01 03:00:00 Europe/Moscow"
Test.specify "should get time of day from offsed-based time" <| Test.specify "should get time of day from offsed-based time" <|
time = parse_datetime "1970-01-01T00:00:01+01:00" . time_of_day time = parse_datetime "1970-01-01T00:00:01+01:00" . time_of_day

View File

@ -34,6 +34,15 @@ spec =
duration = (Duration.new hours=13) - (Duration.new hours=1) duration = (Duration.new hours=13) - (Duration.new hours=1)
duration.hours . should_equal 12 duration.hours . should_equal 12
Test.specify "should render a friendly to display text" <|
Duration.new . to_display_text . should_equal "0s"
Duration.new seconds=30 . to_display_text . should_equal "30s"
Duration.new seconds=30 milliseconds=500 . to_display_text . should_equal "30.5s"
Duration.new hours=1 . to_display_text . should_equal "1h"
Duration.new hours=1 minutes=30 . to_display_text . should_equal "1h 30m"
Duration.new hours=1 seconds=30 . to_display_text . should_equal "1h 0m 30s"
Duration.new hours=1 minutes=30 seconds=30 . to_display_text . should_equal "1h 30m 30s"
Test.specify "should convert to Json" <| Test.specify "should convert to Json" <|
interval = (Duration.new nanoseconds=120) + (Duration.new seconds=30) + (Duration.new hours=14) interval = (Duration.new nanoseconds=120) + (Duration.new seconds=30) + (Duration.new hours=14)
interval.to_json.should_equal <| interval.to_json.should_equal <|

View File

@ -41,4 +41,13 @@ spec =
((Period.new years=1 days=10) == (Period.new years=1 days=10)) . should_be_true ((Period.new years=1 days=10) == (Period.new years=1 days=10)) . should_be_true
((Period.new days=1) != (Period.new months=1)) . should_be_true ((Period.new days=1) != (Period.new months=1)) . should_be_true
Test.specify "should render a friendly to display text" <|
Period.new . to_display_text . should_equal "0D"
Period.new years=2 . to_display_text . should_equal "2Y"
Period.new months=24 . to_display_text . should_equal "2Y"
Period.new months=4 . to_display_text . should_equal "4M"
Period.new months=18 . to_display_text . should_equal "1Y 6M"
Period.new years=2 days=3 . to_display_text . should_equal "2Y 0M 3D"
Period.new days=18 . to_display_text . should_equal "18D"
main = Test_Suite.run_main spec main = Test_Suite.run_main spec

View File

@ -31,14 +31,14 @@ visualization_spec connection =
p_all_rows = ["all_rows_count", all_rows] p_all_rows = ["all_rows_count", all_rows]
p_ixes = ["indices", ixes] p_ixes = ["indices", ixes]
p_ixes_header = ["indices_header", ixes_header] p_ixes_header = ["indices_header", ixes_header]
pairs = [p_header, p_data, p_all_rows, p_ixes, p_ixes_header] pairs = [p_header, p_data, p_all_rows, p_ixes, p_ixes_header, ["type", "Table"]]
JS_Object.from_pairs pairs . to_text JS_Object.from_pairs pairs . to_text
Test.group "Table Visualization" <| Test.group "Table Visualization" <|
Test.specify "should wrap internal errors" <| Test.specify "should wrap internal errors" <|
bad_table = Database_Table.Value Nothing Nothing Nothing Nothing bad_table = Database_Table.Value Nothing Nothing Nothing Nothing
vis = Visualization.prepare_visualization bad_table 2 vis = Visualization.prepare_visualization bad_table 2
json = JS_Object.from_pairs [["error", "Method `set_limit` of Nothing could not be found."]] json = JS_Object.from_pairs [["error", "Method `set_limit` of type Nothing could not be found."]]
vis . should_equal json.to_text vis . should_equal json.to_text
Test.specify "should visualize database tables" <| Test.specify "should visualize database tables" <|
@ -69,30 +69,34 @@ visualization_spec connection =
Test.specify "should handle Vectors" <| Test.specify "should handle Vectors" <|
vis = Visualization.prepare_visualization [1, 2, 3] 2 vis = Visualization.prepare_visualization [1, 2, 3] 2
json = JS_Object.from_pairs [["json", [1, 2]], ["all_rows_count", 3]] json = JS_Object.from_pairs [["type", "Vector"], ["all_rows_count", 3], ["json", [1, 2]]]
vis . should_equal json.to_text vis . should_equal json.to_text
vis2 = Visualization.prepare_visualization [[1, 2], [3, 4]] 2 vis2 = Visualization.prepare_visualization [[1, 2], [3, 4]] 2
json2 = JS_Object.from_pairs [["json", [[1, 2], [3, 4]]], ["all_rows_count", 2]] json2 = JS_Object.from_pairs [["type", "Matrix"], ["all_rows_count", 2], ["json", [[1, 2], [3, 4]]], ["column_count", 2]]
vis2 . should_equal json2.to_text vis2 . should_equal json2.to_text
Test.specify "should handle Arrays" <| Test.specify "should handle Arrays" <|
vis = Visualization.prepare_visualization ([1, 2, 3] . to_array) 2 vis = Visualization.prepare_visualization ([1, 2, 3] . to_array) 2
json = JS_Object.from_pairs [["json", [1, 2]], ["all_rows_count", 3]] json = JS_Object.from_pairs [["type", "Vector"], ["all_rows_count", 3], ["json", [1, 2]]]
vis . should_equal json.to_text vis . should_equal json.to_text
Test.specify "should handle other datatypes" <| Test.specify "should handle other datatypes" <|
vis = Visualization.prepare_visualization (Foo.Value 42) 2 vis = Visualization.prepare_visualization (Foo.Value 42) 2
json = JS_Object.from_pairs [["json", (Foo.Value 42)]] json = JS_Object.from_pairs [["json", JS_Object.from_pairs [["_display_text_", (Foo.Value 42).to_display_text],["x", 42]]]]
vis . should_equal json.to_text vis . should_equal json.to_text
Test.specify "should visualize value type info" <| Test.specify "should visualize value type info" <|
Value_Type.Boolean.to_json . should_equal '{"type":"Value_Type","constructor":"Boolean","_display_text_":"Boolean"}' make_json vt =
Value_Type.Float.to_json . should_equal '{"type":"Value_Type","constructor":"Float","_display_text_":"Float (64 bits)","bits":64}' js_object = vt.to_js_object
Value_Type.Decimal.to_json . should_equal '{"type":"Value_Type","constructor":"Decimal","_display_text_":"Decimal (precision=Nothing, scale=Nothing)","precision":null,"scale":null}' pairs = [["_display_text_", vt.to_display_text]] + vt.to_js_object.field_names.map f-> [f, js_object.get f]
Value_Type.Char.to_json . should_equal '{"type":"Value_Type","constructor":"Char","_display_text_":"Char (max_size=Nothing, variable_length=True)","size":null,"variable_length":true}' JS_Object.from_pairs [["json", JS_Object.from_pairs pairs]] . to_text
Value_Type.Unsupported_Data_Type.to_json . should_equal '{"type":"Value_Type","constructor":"Unsupported_Data_Type","_display_text_":"Unsupported_Data_Type","type_name":null}'
Visualization.prepare_visualization Value_Type.Boolean . should_equal (make_json Value_Type.Boolean)
Visualization.prepare_visualization Value_Type.Float . should_equal (make_json Value_Type.Float)
Visualization.prepare_visualization Value_Type.Decimal . should_equal (make_json Value_Type.Decimal)
Visualization.prepare_visualization Value_Type.Char . should_equal (make_json Value_Type.Char)
Visualization.prepare_visualization Value_Type.Unsupported_Data_Type . should_equal (make_json Value_Type.Unsupported_Data_Type)
spec = spec =
enso_project.data.create_directory enso_project.data.create_directory

View File

@ -1,6 +1,6 @@
import project.Meta
import project.Error.Error
import project.Any.Any import project.Any.Any
import project.Error.Error
import project.Meta
@Builtin_Type @Builtin_Type
@ -50,7 +50,9 @@ type No_Such_Method
method_name self = Meta.meta self.symbol . name method_name self = Meta.meta self.symbol . name
to_display_text self = "Method `"+self.method_name+"` of "+self.target.to_display_text+" could not be found." to_display_text self =
target_type_name = if Meta.is_polyglot self.target then self.target.to_display_text else (Meta.type_of self.target).to_display_text
"Method `"+self.method_name+"` of type "+target_type_name+" could not be found."
@Builtin_Type @Builtin_Type
type Module_Not_In_Package_Error type Module_Not_In_Package_Error

View File

@ -10,6 +10,8 @@ type Primitive
type_of value = @Builtin_Method "Meta.type_of" type_of value = @Builtin_Method "Meta.type_of"
is_polyglot value = @Builtin_Method "Meta.is_polyglot"
# A **very** minimal version of meta for the purpose of tests # A **very** minimal version of meta for the purpose of tests
meta value = meta value =
if is_unresolved_symbol value then Unresolved_Symbol.Value value else if is_unresolved_symbol value then Unresolved_Symbol.Value value else