Skip to content

Commit

Permalink
Merge branch 'develop' into wip/procrat/shift-space-for-fullscreen-vi…
Browse files Browse the repository at this point in the history
…z-6260

* develop:
  Build nightlies every day. (#6681)
  Force `newDashboard` default on the CI-built packages. (#6680)
  Verify ascribed types of parameters really exist (#6584)
  SuggestionBuilder needs to send ascribedType of constructor parameters (#6655)
  Improvements to the Table visualization. (#6653)
  Removing flush (#6670)
  Improving widgets for take/drop (#6641)
  • Loading branch information
Procrat committed May 15, 2023
2 parents f395891 + 1c89430 commit cc7daae
Show file tree
Hide file tree
Showing 50 changed files with 1,005 additions and 170 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
name: Nightly Release
on:
schedule:
- cron: 0 2 * * 2-6
- cron: 0 2 * * *
workflow_dispatch: {}
jobs:
promote-nightly:
Expand Down
14 changes: 7 additions & 7 deletions app/gui/view/documentation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,27 @@
//!
//! [`Tailwind CSS`]: https://tailwindcss.com/
#![recursion_limit = "1024"]
// === Features ===
#![feature(drain_filter)]
#![feature(option_result_contains)]
// === Standard Linter Configuration ===
#![deny(non_ascii_idents)]
#![warn(unsafe_code)]
#![allow(clippy::bool_to_int_with_if)]
#![allow(clippy::let_and_return)]
// === Non-Standard Linter Configuration ===
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(trivial_casts)]
#![warn(trivial_numeric_casts)]
#![warn(unsafe_code)]
#![warn(unused_import_braces)]
#![warn(unused_qualifications)]
#![recursion_limit = "1024"]

use ensogl::prelude::*;
use ensogl::system::web::traits::*;

use graph_editor::component::visualization;
use ide_view_graph_editor as graph_editor;

use enso_frp as frp;
use enso_suggestion_database::documentation_ir::EntryDocumentation;
use ensogl::animation::physics::inertia::Spring;
Expand All @@ -55,16 +53,18 @@ use ensogl::Animation;
use ensogl_component::shadow;
use ensogl_derive_theme::FromTheme;
use ensogl_hardcoded_theme::application::component_browser::documentation as theme;
use graph_editor::component::visualization;
use ide_view_graph_editor as graph_editor;
use web::HtmlElement;
use web::JsCast;

pub mod html;


// ==============
// === Export ===
// ==============

pub mod html;

pub use visualization::container::overlay;


Expand Down
150 changes: 133 additions & 17 deletions app/gui/view/graph-editor/src/builtin/visualization/java_script/table.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,19 @@ class TableVisualization extends Visualization {

constructor(data) {
super(data)
this.setPreprocessor('Standard.Visualization.Table.Visualization', 'prepare_visualization')
this.setRowLimitAndPage(1000, 0)
}

setRowLimitAndPage(row_limit, page) {
if (this.row_limit !== row_limit || this.page !== page) {
this.row_limit = row_limit
this.page = page
this.setPreprocessor(
'Standard.Visualization.Table.Visualization',
'prepare_visualization',
this.row_limit.toString()
)
}
}

onDataReceived(data) {
Expand Down Expand Up @@ -79,21 +91,46 @@ class TableVisualization extends Visualization {
return content
}

function cellRenderer(params) {
if (params.value === null) {
return '<span style="color:grey; font-style: italic;">Nothing</span>'
} else if (params.value === undefined) {
return ''
} else if (params.value === '') {
return '<span style="color:grey; font-style: italic;">Empty</span>'
}
return params.value.toString()
}

if (!this.tabElem) {
while (this.dom.firstChild) {
this.dom.removeChild(this.dom.lastChild)
}

const style =
'.ag-theme-alpine { --ag-grid-size: 3px; --ag-list-item-height: 20px; display: inline; }'
'.ag-theme-alpine { --ag-grid-size: 3px; --ag-list-item-height: 20px; display: inline; }\n' +
'.vis-status-bar { height: 20x; background-color: white; font-size:14px; white-space:nowrap; padding: 0 5px; overflow:hidden; border-radius: 16px }\n' +
'.vis-status-bar > button { width: 12px; margin: 0 2px; display: none }\n' +
'.vis-tbl-grid { height: calc(100% - 20px); width: 100%; }\n'
const styleElem = document.createElement('style')
styleElem.innerHTML = style
this.dom.appendChild(styleElem)

const statusElem = document.createElement('div')
statusElem.setAttributeNS(null, 'id', 'vis-tbl-status')
statusElem.setAttributeNS(null, 'class', 'vis-status-bar')
this.dom.appendChild(statusElem)
this.statusElem = statusElem

const gridElem = document.createElement('div')
gridElem.setAttributeNS(null, 'id', 'vis-tbl-grid')
gridElem.className = 'vis-tbl-grid'
this.dom.appendChild(gridElem)

const tabElem = document.createElement('div')
tabElem.setAttributeNS(null, 'id', 'vis-tbl-view')
tabElem.setAttributeNS(null, 'class', 'scrollable ag-theme-alpine')
this.dom.appendChild(tabElem)
gridElem.appendChild(tabElem)
this.tabElem = tabElem

this.agGridOptions = {
Expand All @@ -106,6 +143,7 @@ class TableVisualization extends Visualization {
resizable: true,
minWidth: 25,
headerValueGetter: params => params.colDef.field,
cellRenderer: cellRenderer,
},
onColumnResized: e => this.lockColumnSize(e),
}
Expand Down Expand Up @@ -198,28 +236,106 @@ class TableVisualization extends Visualization {
dataTruncated = parsedData.all_rows_count !== rowData.length
}

// If the table contains more rows than an upper limit, the engine will send only some of all rows.
// Update Status Bar
this.updateStatusBarControls(
parsedData.all_rows_count === undefined ? 1 : parsedData.all_rows_count,
dataTruncated
)

// If data is truncated, we cannot rely on sorting/filtering so will disable.
// A pinned row is added to tell the user the row count and that filter/sort is disabled.
const col_span = '__COL_SPAN__'
if (dataTruncated) {
columnDefs[0].colSpan = p => p.data[col_span] || 1
}
this.agGridOptions.defaultColDef.filter = !dataTruncated
this.agGridOptions.defaultColDef.sortable = !dataTruncated
this.agGridOptions.api.setColumnDefs(columnDefs)
if (dataTruncated) {
const field = columnDefs[0].field
const extraRow = {
[field]: `Showing ${rowData.length} of ${parsedData.all_rows_count} rows. Sorting and filtering disabled.`,
[col_span]: columnDefs.length,
}
this.agGridOptions.api.setPinnedTopRowData([extraRow])
}
this.agGridOptions.api.setRowData(rowData)
this.updateTableSize(this.dom.getAttributeNS(null, 'width'))
}

makeOption(value, label) {
const optionElem = document.createElement('option')
optionElem.value = value
optionElem.appendChild(document.createTextNode(label))
return optionElem
}

makeButton(label, onclick) {
const buttonElem = document.createElement('button')
buttonElem.name = label
buttonElem.appendChild(document.createTextNode(label))
buttonElem.addEventListener('click', onclick)
return buttonElem
}

// Updates the status bar to reflect the current row limit and page, shown at top of the visualization.
// - Creates the row dropdown and page buttons.
// - Updated the row counts and filter available options.
updateStatusBarControls(all_rows_count, dataTruncated) {
const pageLimit = Math.ceil(all_rows_count / this.row_limit)
if (this.page > pageLimit) {
this.page = pageLimit
}

if (this.statusElem.childElementCount === 0) {
this.statusElem.appendChild(
this.makeButton('«', () => this.setRowLimitAndPage(this.row_limit, 0))
)
this.statusElem.appendChild(
this.makeButton('‹', () => this.setRowLimitAndPage(this.row_limit, this.page - 1))
)

const selectElem = document.createElement('select')
selectElem.name = 'row-limit'
selectElem.addEventListener('change', e => {
this.setRowLimitAndPage(e.target.value, this.page)
})
this.statusElem.appendChild(selectElem)

const rowCountSpanElem = document.createElement('span')
this.statusElem.appendChild(rowCountSpanElem)

this.statusElem.appendChild(
this.makeButton('›', () => this.setRowLimitAndPage(this.row_limit, this.page + 1))
)
this.statusElem.appendChild(
this.makeButton('»', () => this.setRowLimitAndPage(this.row_limit, pageLimit - 1))
)
}

// Enable/Disable Page buttons
this.statusElem.children.namedItem('«').disabled = this.page === 0
this.statusElem.children.namedItem('‹').disabled = this.page === 0
this.statusElem.children.namedItem('›').disabled = this.page === pageLimit - 1
this.statusElem.children.namedItem('»').disabled = this.page === pageLimit - 1

// Update row limit dropdown and row count
const rowCountElem = this.statusElem.getElementsByTagName('span')[0]
const rowLimitElem = this.statusElem.children.namedItem('row-limit')
if (all_rows_count > 1000) {
rowLimitElem.style.display = 'inline-block'
const rowCounts = [1000, 2500, 5000, 10000, 25000, 50000, 100000].filter(
r => r <= all_rows_count
)
if (
all_rows_count < rowCounts[rowCounts.length - 1] &&
rowCounts.indexOf(all_rows_count) === -1
) {
rowCounts.push(all_rows_count)
}
rowLimitElem.innerHTML = ''
rowCounts.forEach(r => {
const option = this.makeOption(r, r.toString())
rowLimitElem.appendChild(option)
})
rowLimitElem.value = this.row_limit

rowCountElem.innerHTML = dataTruncated
? ` of ${all_rows_count} rows (Sorting/Filtering disabled).`
: ` rows.`
} else {
rowLimitElem.style.display = 'none'
rowCountElem.innerHTML = all_rows_count === 1 ? '1 row.' : `${all_rows_count} rows.`
}
}

updateTableSize(clientWidth) {
// Check the grid has been initialised and return if not.
if (!this.agGridOptions) {
Expand Down
8 changes: 8 additions & 0 deletions build/base/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ pub fn write(path: impl AsRef<Path>, contents: impl AsRef<[u8]>) -> Result {
wrappers::write(&path, &contents)
}

/// Read the file file, deserializing JSON text into the data.
#[context("Failed to deserialize JSON from path: {}", path.as_ref().display())]
pub fn read_json<T: DeserializeOwned>(path: impl AsRef<Path>) -> Result<T> {
let contents = read_to_string(&path)?;
serde_json::from_str(&contents)
.with_context(|| format!("Failed to parse JSON from {}", path.as_ref().display()))
}

/// Serialize the data to JSON text and write it to the file.
///
/// See [`write()`].
Expand Down
3 changes: 3 additions & 0 deletions build/build/paths.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
lib/:
client/:
content/:
content-config/:
src/:
config.json:
icons/:
project-manager/:
build/:
Expand Down
4 changes: 2 additions & 2 deletions build/build/src/ci_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,8 @@ pub fn changelog() -> Result<Workflow> {
pub fn nightly() -> Result<Workflow> {
let on = Event {
workflow_dispatch: Some(default()),
// 2am (UTC) from Tuesday to Saturday (i.e. after every workday)
schedule: vec![Schedule::new("0 2 * * 2-6")?],
// 2am (UTC) every day.
schedule: vec![Schedule::new("0 2 * * *")?],
..default()
};

Expand Down
31 changes: 31 additions & 0 deletions build/build/src/project/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,28 @@ impl IsWatcher<Gui> for Watcher {
}
}

/// Override the default value of `newDashboard` in `config.json` to `true`.
///
/// This is a temporary workaround. We want to enable the new dashboard by default in the CI-built
/// IDE, but we don't want to enable it by default in the IDE built locally by developers.
pub fn override_default_for_authentication(
path: &crate::paths::generated::RepoRootAppIdeDesktopLibContentConfigSrcConfigJson,
) -> Result {
let json_path = ["groups", "featurePreview", "options", "newDashboard", "value"];
let mut json = ide_ci::fs::read_json::<serde_json::Value>(path)?;
let mut current =
json.as_object_mut().ok_or_else(|| anyhow!("Failed to find object in {:?}", path))?;
for key in &json_path[..json_path.len() - 1] {
current = current
.get_mut(*key)
.with_context(|| format!("Failed to find {key:?} in {path:?}"))?
.as_object_mut()
.with_context(|| format!("Failed to find object at {key:?} in {path:?}"))?;
}
current.insert(json_path.last().unwrap().to_string(), serde_json::Value::Bool(true));
ide_ci::fs::write_json(path, &json)?;
Ok(())
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Gui;
Expand All @@ -109,6 +131,15 @@ impl IsTarget for Gui {
) -> BoxFuture<'static, Result<Self::Artifact>> {
let WithDestination { inner, destination } = job;
async move {
// TODO: [mwu]
// This is a temporary workaround until https://github.com/enso-org/enso/issues/6662
// is resolved.
if ide_ci::actions::workflow::is_in_env() {
let path = &context.repo_root.app.ide_desktop.lib.content_config.src.config_json;
warn!("Overriding default for authentication in {}", path.display());
override_default_for_authentication(path)?;
}

let ide = ide_desktop_from_context(&context);
let wasm = Wasm.get(context, inner.wasm);
let content_env =
Expand Down
2 changes: 2 additions & 0 deletions distribution/lib/Standard/Base/0.0.0-dev/src/Data/Array.enso
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ type Array
If an `Index_Sub_Range`, then the selection is interpreted following
the rules of that type.
If a `Range`, the selection is specified by two indices, from and to.
@range Index_Sub_Range.default_widget
take : (Index_Sub_Range | Range | Integer) -> Vector Any
take self range=(Index_Sub_Range.First 1) = Vector.take self range

Expand All @@ -251,6 +252,7 @@ type Array
If an `Index_Sub_Range`, then the selection is interpreted following
the rules of that type.
If a `Range`, the selection is specified by two indices, from and to.
@range Index_Sub_Range.default_widget
drop : (Index_Sub_Range | Range | Integer) -> Vector Any
drop self range=(Index_Sub_Range.First 1) = Vector.drop self range

Expand Down
Loading

0 comments on commit cc7daae

Please sign in to comment.