Allow left/right for collapse, more hotkeys

* Use 'b' to toggle fold icon before/after key
* Use 'g' to toggle indentation guide lines
* Cosmetic fixes
This commit is contained in:
Chris 2024-10-03 15:29:56 +02:00
parent f023de0198
commit ddfefa124b
4 changed files with 77 additions and 9 deletions

View File

@ -174,8 +174,8 @@ class Editor
'value' => "Insert Value", 'value' => "Insert Value",
'object' => "Insert Object{}", 'object' => "Insert Object{}",
'array' => "Insert Array[]", 'array' => "Insert Array[]",
'sep0' => "---", //'sep0' => "---",
'__paste' => "Paste from clipboard", //'__paste' => "Paste from clipboard",
], 'Insert'); ], 'Insert');
$this->redrawInfoBar([ '^C' => 'Cancel', '↑/↓' => 'Select option', 'Enter' => 'Accept' ]); $this->redrawInfoBar([ '^C' => 'Cancel', '↑/↓' => 'Select option', 'Enter' => 'Accept' ]);
$sel = $menu->display(0, 0, 30, 0, "value"); $sel = $menu->display(0, 0, 30, 0, "value");
@ -213,6 +213,18 @@ class Editor
$this->running = false; $this->running = false;
break; break;
case "g":
Settings::$indentationGuides = !Settings::$indentationGuides;
$this->redrawEditor();
$this->showMessage("Toggle indentation guides (g)");
break;
case "b":
Settings::$collapseBefore = !Settings::$collapseBefore;
$this->redrawEditor();
$this->showMessage("Toggle collapse icon before item (b)");
break;
case "+": case "+":
$node = $this->list->getNodeForIndex($this->currentRow); $node = $this->list->getNodeForIndex($this->currentRow);
if ($node instanceof CollapsibleNode) { if ($node instanceof CollapsibleNode) {
@ -221,16 +233,36 @@ class Editor
$this->redrawEditor(); $this->redrawEditor();
} }
break; break;
case "k{LEFT}":
$node = $this->list->getNodeForIndex($this->currentRow);
if ($node instanceof CollapsibleNode) {
$node->collapse(true);
$this->list->parseTree();
$this->redrawEditor();
}
break;
case "k{RIGHT}":
$node = $this->list->getNodeForIndex($this->currentRow);
if ($node instanceof CollapsibleNode) {
$node->collapse(false);
$this->list->parseTree();
$this->redrawEditor();
}
break;
case "q": case "q":
Settings::$editorQuotedKeys = !Settings::$editorQuotedKeys; Settings::$editorQuotedKeys = !Settings::$editorQuotedKeys;
$this->redrawEditor(); $this->redrawEditor();
$this->showMessage("Toggle quoted keys (q)");
break; break;
case "c": case "c":
Settings::$compactGroups = !Settings::$compactGroups; Settings::$compactGroups = !Settings::$compactGroups;
$this->list->parseTree(); $this->list->parseTree();
$this->redrawEditor(); $this->redrawEditor();
$this->showMessage("Toggle compact groups (c)");
break; break;
case "\x0e": // ctrl-n case "\x0e": // ctrl-n

View File

@ -39,7 +39,7 @@ class Menu
[$w,$h] = $this->terminal->getSize(); [$w,$h] = $this->terminal->getSize();
if ($height == 0) { if ($height == 0) {
$height = min($h - 5, count($this->items) + 4); $height = min($h - 5, count($this->items) + 2);
} }
if ($left == 0) { if ($left == 0) {
$left = round(($w - $width) / 2); $left = round(($w - $width) / 2);

View File

@ -5,6 +5,7 @@ namespace NoccyLabs\JsonEdit\List;
use Countable; use Countable;
use NoccyLabs\JsonEdit\Settings; use NoccyLabs\JsonEdit\Settings;
use NoccyLabs\JsonEdit\Tree\ArrayNode; use NoccyLabs\JsonEdit\Tree\ArrayNode;
use NoccyLabs\JsonEdit\Tree\CollapsibleNode;
use NoccyLabs\JsonEdit\Tree\Tree; use NoccyLabs\JsonEdit\Tree\Tree;
use NoccyLabs\JsonEdit\Tree\Node; use NoccyLabs\JsonEdit\Tree\Node;
use NoccyLabs\JsonEdit\Tree\ObjectNode; use NoccyLabs\JsonEdit\Tree\ObjectNode;
@ -15,6 +16,11 @@ class TreeList implements Countable
/** @var array<string,Entry> */ /** @var array<string,Entry> */
public array $list = []; public array $list = [];
private const TREE_FOLD_OPEN = "\u{f0fe}";
private const TREE_FOLD_CLOSE = "\u{f146}";
private const TREE_INDENT_GUIDE = "\u{258f} ";
private const TREE_INDENT = " ";
public function __construct(private Tree $tree) public function __construct(private Tree $tree)
{ {
} }
@ -151,7 +157,10 @@ class TreeList implements Countable
} }
echo "\e[{$screenRow};1H\e[0m"; echo "\e[{$screenRow};1H\e[0m";
echo ($selected?"\e[44;97m":"\e[0;37m")."\e[K"; echo ($selected?"\e[44;97m":"\e[0;37m")."\e[K";
echo "\e[90m".str_repeat("\u{258f} ",$entry->depth)."\e[37m"; echo "\e[90m".str_repeat(
Settings::$indentationGuides ? self::TREE_INDENT_GUIDE : self::TREE_INDENT,
$entry->depth
)."\e[37m";
if ($entry->closer) { if ($entry->closer) {
if (!Settings::$compactGroups) { if (!Settings::$compactGroups) {
@ -164,6 +173,18 @@ class TreeList implements Countable
return; return;
} }
if (Settings::$collapseBefore) {
if ($entry->node instanceof CollapsibleNode) {
if ($entry->node->isCollapsed()) {
//echo "\e[90m\u{25ba} \e[37m";
echo "\e[90m".self::TREE_FOLD_OPEN." \e[37m";
} else {
//echo "\e[90m\u{25bc} \e[37m";
echo "\e[90m".self::TREE_FOLD_CLOSE." \e[37m";
}
}
}
if (!is_null($entry->key)) { if (!is_null($entry->key)) {
echo (is_int($entry->key) echo (is_int($entry->key)
?"\e[36;2m\u{e0b6}\e[7m#{$entry->key}\e[27m\u{e0b4}\e[22;37m " ?"\e[36;2m\u{e0b6}\e[7m#{$entry->key}\e[27m\u{e0b4}\e[22;37m "
@ -175,16 +196,20 @@ class TreeList implements Countable
if ($entry->node instanceof ArrayNode) { if ($entry->node instanceof ArrayNode) {
echo "[" . (Settings::$compactGroups ? "…]":""); echo "[" . (Settings::$compactGroups ? "…]":"");
if ($entry->node->isCollapsed()) { if ($entry->node->isCollapsed()) {
echo " \e[90m\u{25ba} \e[2m[".count($entry->node->items)."]\e[22m"; if (!Settings::$collapseBefore) echo " \e[90m\u{25ba}";
echo " \e[90;2m[".count($entry->node->items)."]\e[22m";
if (!Settings::$compactGroups) echo "\e[37m ]";
} else { } else {
echo " \e[90m\u{25bc}"; if (!Settings::$collapseBefore) echo " \e[90m\u{25bc}";
} }
} elseif ($entry->node instanceof ObjectNode) { } elseif ($entry->node instanceof ObjectNode) {
echo "{" . (Settings::$compactGroups ? "…}":""); echo "{" . (Settings::$compactGroups ? "…}":"");
if ($entry->node->isCollapsed()) { if ($entry->node->isCollapsed()) {
echo " \e[90m\u{25ba} \e[2m".join(", ",array_keys($entry->node->properties))."\e[22m"; if (!Settings::$collapseBefore) echo " \e[90m\u{25ba}";
echo " \e[90;2;3m".join(", ",array_keys($entry->node->properties))."\e[22;23m";
if (!Settings::$compactGroups) echo "\e[37m }";
} else { } else {
echo " \e[90m\u{25bc}"; if (!Settings::$collapseBefore) echo " \e[90m\u{25bc}";
} }
} elseif ($entry->node instanceof ValueNode) { } elseif ($entry->node instanceof ValueNode) {
$value = $entry->node->value; $value = $entry->node->value;

View File

@ -8,12 +8,20 @@ class Settings
public static bool $compactGroups = false; public static bool $compactGroups = false;
public static bool $indentationGuides = true;
public static bool $collapseBefore = true;
public static bool $highlightRow = true;
public static function load(string $filename): void public static function load(string $filename): void
{ {
if (file_exists($filename) && is_readable($filename)) { if (file_exists($filename) && is_readable($filename)) {
$data = json_decode(file_get_contents($filename)); $data = json_decode(file_get_contents($filename));
self::$editorQuotedKeys = $data->editorQuotedKeys ?? false; self::$editorQuotedKeys = $data->editorQuotedKeys ?? false;
self::$compactGroups = $data->compactGroups ?? false; self::$compactGroups = $data->compactGroups ?? false;
self::$indentationGuides = $data->indentationGuides ?? true;
self::$collapseBefore = $data->collapseBefore ?? true;
} }
} }
@ -22,6 +30,9 @@ class Settings
$data = json_encode([ $data = json_encode([
'editorQuotedKeys' => self::$editorQuotedKeys, 'editorQuotedKeys' => self::$editorQuotedKeys,
'compactGroups' => self::$compactGroups, 'compactGroups' => self::$compactGroups,
'indentationGuides' => self::$indentationGuides,
'collapseBefore' => self::$collapseBefore,
'highlightRow' => self::$highlightRow,
], JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT)."\n"; ], JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT)."\n";
@file_put_contents($filename, $data); @file_put_contents($filename, $data);
} }