diff --git a/pyproject.toml b/pyproject.toml index 4f317f9..9c8b241 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,7 +15,7 @@ classifiers = [ "Operating System :: OS Independent", ] dependencies = [ - "textual >= 0.79", + "textual[syntax] >= 0.79", "rich", "platformdirs", "pyaml", diff --git a/src/production_planner/DataView.tcss b/src/production_planner/DataView.tcss new file mode 100644 index 0000000..c9d01a4 --- /dev/null +++ b/src/production_planner/DataView.tcss @@ -0,0 +1,4 @@ +DataView > Horizontal { + width: auto; + height: auto; +} diff --git a/src/production_planner/__init__.py b/src/production_planner/__init__.py index 711cd10..3389e17 100644 --- a/src/production_planner/__init__.py +++ b/src/production_planner/__init__.py @@ -82,6 +82,9 @@ def get_system_commands(self, screen: Screen) -> Iterable[SystemCommand]: yield planner_command("Save As", "Save the currently active file with a new filename", self._save_as) yield planner_command("Load", "Load a new file in the currently active table", self._load) yield planner_command("Delete", "Delete a file from the filesystem", self._delete) + # 3, because the command-palette is the second screen + if len(self.screen_stack) < 3: + yield planner_command("Dataview", "Shows the corresponding yaml source of the open file", self._dataview) def _save_as(self): if not self.focused_table: @@ -99,6 +102,11 @@ def _delete(self): return self.call_next(self.focused_table.action_delete) + def _dataview(self): + if not self.focused_table or len(self.screen_stack) > 2: + return + self.call_next(self.focused_table.action_dataview) + def is_table_shown(self, table: PlannerTable) -> bool: raise NotImplemented diff --git a/src/production_planner/datatable.py b/src/production_planner/datatable.py index 506ba08..c95d803 100644 --- a/src/production_planner/datatable.py +++ b/src/production_planner/datatable.py @@ -34,6 +34,8 @@ SaveDataFile ) +from .dataview import DataView + import os from dataclasses import dataclass from pathlib import Path @@ -164,6 +166,9 @@ def on_mount(self) -> None: def on_focus(self): self.app.focused_table = self + def action_dataview(self): + self.app.push_screen(DataView(self)) + def save_data(self, subpath=None) -> Optional[Tuple[DataFile]]: result = self.sink.sink_commit(subpath) self.app.title = self.sink.title diff --git a/src/production_planner/dataview.py b/src/production_planner/dataview.py new file mode 100644 index 0000000..42db4f8 --- /dev/null +++ b/src/production_planner/dataview.py @@ -0,0 +1,60 @@ +#! /bin/env python +# -*- coding:utf-8 -*- +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +from io import StringIO +from difflib import unified_diff + +from textual.screen import Screen +from textual.containers import Horizontal, Vertical +from textual.widgets import ( + TextArea, + Label, + Footer, +) +from textual.app import ComposeResult + +import yaml + + +class YamlEditor(TextArea): + ... + + +class DiffEditor(TextArea): + ... + + +class DataView(Screen): + BINDINGS = [ + ("escape", "cancel", "Cancel"), + ] + CSS_PATH = "DataView.tcss" + + def __init__(self, table, *args, **kwargs): + self.table = table + super().__init__(*args, **kwargs) + + def compose(self) -> ComposeResult: + with Horizontal(): + with Vertical(): + yield Label("File in session") + # FIXME: when `editor.read_only == False` then the `escape` > `action_cancel` binding doesn't work anymore + new = yaml.dump(self.table.nodetree) + yield YamlEditor.code_editor(new, language="yaml", read_only=True) + + with Vertical(): + old = yaml.dump(self.table.sink.sink._data) + diff = StringIO() + diff.writelines(unified_diff(old.splitlines(keepends=True), + new.splitlines(keepends=True), + fromfile="sink.yaml", + tofile="staging.yaml")) + yield Label("Difference to saved file") + yield DiffEditor.code_editor(diff.getvalue(), language="yaml", read_only=True) + yield Footer() + + def action_cancel(self): + self.dismiss("")