Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add explore operator and table format (TUI) #3113

Open
wants to merge 84 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 73 commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
f2744ee
Add tui operator scaffold
mavam Apr 21, 2023
f1a42e5
Bring back old pipeline explorer component
mavam Apr 22, 2023
6a1644c
Make dataset thread-safe and move it into UI state
mavam Apr 28, 2023
d1d499b
Make table building fully incremental
mavam Apr 29, 2023
3c194e1
Refactor theme architecture
mavam Apr 30, 2023
72c7c44
Assign colors for mocha theme
mavam Apr 30, 2023
b03bf78
Remove scroll indicator
mavam Apr 30, 2023
252a32a
Add options
mavam Apr 30, 2023
672ccbe
Add documentation
mavam Apr 30, 2023
58d60ee
Fix typo
mavam May 1, 2023
a635e43
Add utility function to compute record depth
mavam May 1, 2023
fde9e81
Support fully nested records
mavam May 1, 2023
6663f82
Prettify help
mavam May 1, 2023
4de5343
Refactor for better modularity
mavam May 2, 2023
07664d7
Use more trailing return types
mavam May 2, 2023
90a9d50
Skip empty table slices
mavam May 2, 2023
724280d
Make menu focus visible
mavam May 2, 2023
fba3842
Fix multi-schema rendering
mavam May 2, 2023
f9ed2be
Render separator all the way to the bottom
mavam May 2, 2023
b3cfaf2
Make the schema menu navigable
mavam May 3, 2023
80c46e8
Fix cell focusing
mavam May 3, 2023
aa5bd2b
Remove confusing separator
mavam May 3, 2023
f345256
Fix scrolling and focus reallocation
mavam May 3, 2023
cc044aa
Use a colored split separator
mavam May 3, 2023
7dd53e9
Make clang-format happy
mavam May 3, 2023
611ca5c
Add FTXUI as submodule
mavam May 4, 2023
e87c607
Work around non-target-specific settings
mavam May 10, 2023
35712d2
Remove row ID special casing
mavam May 11, 2023
7b6ce01
Bump FTXUI to v4.1.0
mavam May 24, 2023
8d1998a
Use packaged ftxui for the static binary build
tobim May 21, 2023
32ba136
Streamline component implementation
mavam Jun 14, 2023
801f5ac
Rename plugin from 'tui' to 'explore'
mavam Jun 14, 2023
6259305
Remove dead code
mavam Jun 14, 2023
47bdd6a
Remove unneeded argument
mavam Jun 14, 2023
9cc73b3
Implement collapsing of nested records
mavam Jun 14, 2023
a50fee6
Adapt to new plugin API
mavam Jul 14, 2023
688477d
Switch to static cells initially
mavam Jul 15, 2023
fb17c47
Factor Navigator from Explorer
mavam Jul 15, 2023
160924a
Comment currently unused building blocks
mavam Jul 15, 2023
0d38eac
Refactor helper utility
mavam Jul 16, 2023
6257204
Redo ASCII logo
mavam Jul 16, 2023
8de8580
Refactor Help component
mavam Jul 16, 2023
088002f
Allow Navigator at multiple positions
mavam Jul 16, 2023
c0036df
Fix invalid access bug
mavam Jul 16, 2023
d1c7ac5
Fix Explorer initialization window
mavam Jul 16, 2023
f8c62f9
Merge branch 'origin/main' into topic/tui-take-two
mavam Jul 16, 2023
f93179a
Adapt to The Big Rename
mavam Jul 16, 2023
00fc2bd
Fix invalid assertion
mavam Jul 16, 2023
dcb57a8
Switch to Rosé Pine color scheme
mavam Jul 17, 2023
df5a31c
Fix navigator alignment
mavam Jul 17, 2023
21c8195
Document navigator options
mavam Jul 17, 2023
bd0e7c2
Support hiding type information in column header
mavam Jul 17, 2023
aa0079b
Support auto-hiding of Navigator
mavam Jul 17, 2023
df8e6ba
Make clang-format happy
mavam Jul 17, 2023
0c12c37
Fix Nix packaging
mavam Jul 17, 2023
f1d2e1c
Fix a few warnings
mavam Jul 17, 2023
eb20a19
Try fixing Docker build in CI (-Werror)
mavam Jul 17, 2023
9586ee1
Remove unneeded state
mavam Jul 20, 2023
63034c6
Add a "table" format
mavam Jul 20, 2023
2c1ff22
Update FTXUI to latest main
mavam Jul 21, 2023
b0a7b54
Make clang-format happy
mavam Jul 21, 2023
b2170da
Merge branch 'origin/main' into topic/tui-take-two
mavam Jul 21, 2023
1894a1d
Abort when not on terminal
mavam Jul 21, 2023
b7d3bbc
Perform small logic tweaks
mavam Jul 22, 2023
985b3ea
Make Navigator a separate Pane
mavam Jul 22, 2023
e267dbe
Fix Explorer keyboard navigation
mavam Jul 22, 2023
5b4f7d8
Make screen fitting the default
mavam Jul 22, 2023
b8668bc
Prettify loading experience
mavam Jul 22, 2023
570e7c2
Enable toggling of column types via keyboard
mavam Jul 22, 2023
971f0db
Make fixed-size component rendering more flexible
mavam Jul 23, 2023
60449a0
Make it possible to toggle Navigator via keypress
mavam Jul 23, 2023
8100519
Remove static cell
mavam Jul 23, 2023
93b444d
Perform minor code cleanups
mavam Jul 23, 2023
0e44fe7
Prepare for horizontal tables
mavam Jul 23, 2023
5a6fec2
Introduce shared 'tui' library
mavam Jul 24, 2023
4977f30
Merge origin/main into topic/tui-take-two
mavam Jul 24, 2023
cfb46b5
Update documentation
mavam Jul 24, 2023
45cfe00
Merge 'origin/main' into topic/tui-take-two
mavam Aug 6, 2023
b9ff58d
Adapt to upstream API changes
mavam Aug 6, 2023
e69b29d
Change shape and alignment of null values
mavam Aug 6, 2023
33ff35c
Bump FTXUI submodule to v5.0.0
mavam Aug 23, 2023
552ff65
Merge 'origin/main' into topic/tui-take-two
mavam Aug 23, 2023
9a1c7da
Merge remote-tracking branch 'origin/main' into topic/tui-take-two
mavam Sep 12, 2023
812e35c
Merge branch 'main' into topic/tui-take-two
mavam Sep 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@
[submodule "libtenzir/aux/caf"]
path = libtenzir/aux/caf
url = https://github.com/tenzir/actor-framework
[submodule "plugins/tui/aux/ftxui"]
path = plugins/explore/aux/ftxui
url = https://github.com/ArthurSonzogni/FTXUI.git
9 changes: 0 additions & 9 deletions libtenzir/builtins/formats/pcap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,12 +501,6 @@ class pcap_printer final : public plugin_printer {
class plugin final : public virtual parser_plugin<pcap_parser>,
public virtual printer_plugin<pcap_printer> {
public:
auto initialize(const record& config, const record& /* global_config */)
-> caf::error override {
config_ = config;
return caf::none;
}

auto parse_parser(parser_interface& p) const
-> std::unique_ptr<plugin_parser> override {
auto parser = argument_parser{
Expand All @@ -531,9 +525,6 @@ class plugin final : public virtual parser_plugin<pcap_parser>,
auto name() const -> std::string override {
return "pcap";
}

private:
record config_;
};

} // namespace
Expand Down
5 changes: 4 additions & 1 deletion libtenzir/include/tenzir/type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1231,12 +1231,15 @@ class record_type final : public stateful_type_base {
/// Returns an iterable view over the leaf fields of a record type.
[[nodiscard]] generator<leaf_view> leaves() const noexcept;

/// Returns the numnber of fields in a record.
/// Returns the number of fields in a record.
[[nodiscard]] size_t num_fields() const noexcept;

/// Returns the number of leaf fields in a record.
[[nodiscard]] size_t num_leaves() const noexcept;

/// Returns the number of nested sub-records.
[[nodiscard]] size_t depth() const noexcept;

/// Resolves a flat index into an offset.
[[nodiscard]] offset resolve_flat_index(size_t flat_index) const noexcept;

Expand Down
58 changes: 58 additions & 0 deletions libtenzir/src/type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2656,6 +2656,64 @@ size_t record_type::num_leaves() const noexcept {
return num_leaves;
}

// TODO: the implementation is copied from above. We may want to factor the two
// functions.
size_t record_type::depth() const noexcept {
auto index = offset{0};
auto record_depth = index.size();
auto history = detail::stack_vector<const fbs::type::RecordType*, 64>{
table().type_as_record_type()};
while (!index.empty()) {
const auto* record = history.back();
TENZIR_ASSERT(record);
const auto* fields = record->fields();
TENZIR_ASSERT(fields);
// This is our exit condition: If we arrived at the end of a record, we need
// to step out one layer. We must also reset the target key at this point.
if (index.back() >= fields->size()) {
history.pop_back();
index.pop_back();
if (!index.empty())
++index.back();
continue;
}
const auto* field = record->fields()->Get(index.back());
TENZIR_ASSERT(field);
const auto* field_type = resolve_transparent(field->type_nested_root());
TENZIR_ASSERT(field_type);
switch (field_type->type_type()) {
case fbs::type::Type::pattern_type:
__builtin_unreachable();
case fbs::type::Type::NONE:
case fbs::type::Type::bool_type:
case fbs::type::Type::int64_type:
case fbs::type::Type::uint64_type:
case fbs::type::Type::double_type:
case fbs::type::Type::duration_type:
case fbs::type::Type::time_type:
case fbs::type::Type::string_type:
case fbs::type::Type::ip_type:
case fbs::type::Type::subnet_type:
case fbs::type::Type::enumeration_type:
case fbs::type::Type::list_type:
case fbs::type::Type::map_type: {
++index.back();
break;
}
case fbs::type::Type::record_type: {
history.emplace_back(field_type->type_as_record_type());
index.push_back(0);
record_depth = std::max(record_depth, index.size());
break;
}
case fbs::type::Type::enriched_type:
__builtin_unreachable();
break;
}
}
return record_depth;
}

offset record_type::resolve_flat_index(size_t flat_index) const noexcept {
size_t current_flat_index = 0;
auto index = offset{0};
Expand Down
11 changes: 11 additions & 0 deletions nix/overlay.nix
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ in {
# spdlog in nixpkgs uses `fmt_8` directly, but we want version 9, so we use a
# little hack here.
fmt_8 = prev.fmt;
ftxui =
if !isStatic
then prev.ftxui
else
prev.ftxui.overrideAttrs (orig: {
cmakeFlags = [
"-DFTXUI_BUILD_EXAMPLES=OFF"
"-DFTXUI_BUILD_DOCS=OFF"
"-DFTXUI_BUILD_TESTS=OFF"
];
});
# We need boost 1.8.1 at minimum for URL.
boost = prev.boost18x;
rapidjson = prev.rapidjson.overrideAttrs (_: {
Expand Down
3 changes: 3 additions & 0 deletions nix/tenzir/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
arrow-cpp,
fast_float,
flatbuffers,
ftxui,
spdlog,
libyamlcpp,
simdjson,
Expand Down Expand Up @@ -57,6 +58,7 @@
extraPlugins' = map (x: "extra-plugins/${baseNameOf x}") extraPlugins;
bundledPlugins =
[
"plugins/explore"
"plugins/kafka"
"plugins/nic"
"plugins/parquet"
Expand Down Expand Up @@ -90,6 +92,7 @@
buildInputs = [
boost
fast_float
ftxui
libpcap
libunwind
libyamlcpp
Expand Down
48 changes: 48 additions & 0 deletions plugins/explore/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
cmake_minimum_required(VERSION 3.19...3.24 FATAL_ERROR)

project(
explore
DESCRIPTION "explore plugin for Tenzir"
LANGUAGES CXX)

include(CTest)

find_package(Tenzir REQUIRED)

file(GLOB_RECURSE explore_sources CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")

TenzirRegisterPlugin(
TARGET explore
ENTRYPOINT src/plugin.cpp
SOURCES ${explore_sources}
INCLUDE_DIRECTORIES include)

option(TENZIR_ENABLE_BUNDLED_FTXUI "Always use the bundled ftxui" OFF)
add_feature_info("TENZIR_ENABLE_BUNDLED_FTXUI" TENZIR_ENABLE_BUNDLED_FTXUI
"always use the bundled ftxui.")
if (NOT TENZIR_ENABLE_BUNDLED_FTXUI)
find_package(ftxui QUIET)
if (ftxui_FOUND)
string(APPEND TENZIR_FIND_DEPENDENCY_LIST
"\nfind_package(ftxui REQUIRED)")
dependency_summary("ftxui" ftxui::screen "Dependencies")
endif ()
endif ()
if (NOT ftxui_FOUND)
if (NOT TENZIR_ENABLE_BUNDLED_FTXUI)
message(
STATUS
"Cannot find installed ftxui; falling back to bundled version")
endif ()
unset(COMPILE_WARNING_AS_ERROR)
set(CMAKE_CXX_STANDARD 20)
add_subdirectory(aux/ftxui)
set(COMPILE_WARNING_AS_ERROR)
unset(CMAKE_CXX_STANDARD)
dependency_summary("ftxui" "${CMAKE_CURRENT_SOURCE_DIR}/aux/ftxui"
"Dependencies")
endif ()

target_link_libraries(explore PUBLIC tenzir::libtenzir tenzir::internal)
target_link_libraries(explore PUBLIC ftxui::screen ftxui::dom ftxui::component)
1 change: 1 addition & 0 deletions plugins/explore/aux/ftxui
Submodule ftxui added at 43240a
94 changes: 94 additions & 0 deletions plugins/explore/include/explore/components.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// _ _____ __________
// | | / / _ | / __/_ __/ Visibility
// | |/ / __ |_\ \ / / Across
// |___/_/ |_/___/ /_/ Space and Time
//
// SPDX-FileCopyrightText: (c) 2023 The Tenzir Contributors
// SPDX-License-Identifier: BSD-3-Clause

#pragma once

#include "explore/ui_state.hpp"

#include <tenzir/fwd.hpp>
#include <tenzir/view.hpp>

#include <ftxui/component/component.hpp>
#include <ftxui/dom/elements.hpp>

namespace tenzir::plugins::explore {

/// Lifts an FTXUI element into a component.
auto lift(ftxui::Element e) -> ftxui::Component;

/// Makes a component a vertically scrollable in a frame.
auto enframe(const ftxui::Component& component) -> ftxui::Component;

// We are adding a "deep" event catching helper here becase we are facing the
// same issue of a parent component masking the events from its children as
// reported in https://github.com/ArthurSonzogni/FTXUI/discussions/428.

enum class catch_policy {
child,
parent,
};

template <catch_policy Policy>
class CatchBase : public ftxui::ComponentBase {
public:
// Constructor.
explicit CatchBase(std::function<bool(ftxui::Event)> on_event)
: on_event_{std::move(on_event)} {
}

auto OnEvent(ftxui::Event event) -> bool override {
if constexpr (Policy == catch_policy::child)
return ComponentBase::OnEvent(event) || on_event_(event);
else
return on_event_(event) || ComponentBase::OnEvent(event);
}

protected:
std::function<bool(ftxui::Event)> on_event_;
};

template <catch_policy Policy>
auto Catch(ftxui::Component child,
std::function<bool(ftxui::Event event)> on_event)
-> ftxui::Component {
auto out = Make<CatchBase<Policy>>(std::move(on_event));
out->Add(std::move(child));
return out;
}

template <catch_policy Policy>
auto Catch(std::function<bool(ftxui::Event)> on_event)
-> ftxui::ComponentDecorator {
return [on_event = std::move(on_event)](ftxui::Component child) {
return Catch<Policy>(std::move(child),
[on_event = on_event](ftxui::Event event) {
return on_event(std::move(event));
});
};
}

/// A major UI component in a focusable, bordered frame.
auto Pane(ui_state* state, ftxui::Component component) -> ftxui::Component;

/// A data frame.
auto DataFrame(ui_state* state, const type& schema) -> ftxui::Component;

/// The schema navigator.
auto Navigator(ui_state* state, int* index, int* width) -> ftxui::Component;

/// The Explorer.
auto Explorer(ui_state* state) -> ftxui::Component;

/// The top-level component of the application.
/// @param screen The screen to hook for UI events.
/// @param state The UI state.
/// @returns The FTXUI component.
auto MainWindow(ftxui::ScreenInteractive* screen, ui_state* state)
-> ftxui::Component;

} // namespace tenzir::plugins::explore
18 changes: 18 additions & 0 deletions plugins/explore/include/explore/elements.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// _ _____ __________
// | | / / _ | / __/_ __/ Visibility
// | |/ / __ |_\ \ / / Across
// |___/_/ |_/___/ /_/ Space and Time
//
// SPDX-FileCopyrightText: (c) 2023 The Tenzir Contributors
// SPDX-License-Identifier: BSD-3-Clause

#pragma once

#include <ftxui/dom/elements.hpp>

namespace tenzir::plugins::explore {

/// Renders the Tenzir logo.
auto logo() -> ftxui::Element;

} // namespace tenzir::plugins::explore
38 changes: 38 additions & 0 deletions plugins/explore/include/explore/operator_args.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// _ _____ __________
// | | / / _ | / __/_ __/ Visibility
// | |/ / __ |_\ \ / / Across
// |___/_/ |_/___/ /_/ Space and Time
//
// SPDX-FileCopyrightText: (c) 2023 The Tenzir Contributors
// SPDX-License-Identifier: BSD-3-Clause

#pragma once

#include <tenzir/location.hpp>

#include <optional>
#include <string>

namespace tenzir::plugins::explore {

/// The plugin configuration.
struct operator_args {
std::optional<located<int>> width;
std::optional<located<int>> height;
std::optional<location> fullscreen;
std::optional<location> hide_types;
std::optional<located<std::string>> navigator_position;
std::optional<location> navigator_auto_hide;

friend auto inspect(auto& f, operator_args& x) -> bool {
return f.object(x)
.pretty_name("operator_args")
.fields(f.field("width", x.width), f.field("height", x.height),
f.field("fullscreen", x.fullscreen),
f.field("hide_types", x.hide_types),
f.field("navigator_position", x.navigator_position),
f.field("navigator_auto_hide", x.navigator_auto_hide));
}
};

} // namespace tenzir::plugins::explore
34 changes: 34 additions & 0 deletions plugins/explore/include/explore/printer_args.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// _ _____ __________
// | | / / _ | / __/_ __/ Visibility
// | |/ / __ |_\ \ / / Across
// |___/_/ |_/___/ /_/ Space and Time
//
// SPDX-FileCopyrightText: (c) 2023 The Tenzir Contributors
// SPDX-License-Identifier: BSD-3-Clause

#pragma once

#include <tenzir/location.hpp>

#include <optional>
#include <string>

namespace tenzir::plugins::explore {

/// The operator configuration.
struct printer_args {
std::optional<located<int>> width;
std::optional<located<int>> height;
bool real_time;
bool hide_types;

friend auto inspect(auto& f, printer_args& x) -> bool {
return f.object(x)
.pretty_name("printer_args")
.fields(f.field("width", x.width), f.field("height", x.height),
f.field("real-time", x.real_time),
f.field("hide_types", x.hide_types));
}
};

} // namespace tenzir::plugins::explore