jsonedit/src/List/TreeList.php

186 lines
5.6 KiB
PHP

<?php
namespace NoccyLabs\JEdit\List;
use Countable;
use NoccyLabs\JEdit\Tree\ArrayNode;
use NoccyLabs\JEdit\Tree\Tree;
use NoccyLabs\JEdit\Tree\Node;
use NoccyLabs\JEdit\Tree\ObjectNode;
use NoccyLabs\JEdit\Tree\ValueNode;
class TreeList implements Countable
{
/** @var array<string,Entry> */
public array $list = [];
public function __construct(private Tree $tree)
{
}
public function count(): int
{
return count($this->list);
}
public function parseTree(): void
{
$this->list = [];
$this->parseNode($this->tree->root, [], null);
}
private function parseNode(Node $node, array $path, string|int|null $key): void
{
if (str_contains($key, '/')) {
$ekey = urlencode($key);
} else {
$ekey = $key;
}
$level = count($path);
$entryKey = join("/", $path) . (is_null($key) ? "/" : (is_int($key) ? sprintf("/%d", $key) : sprintf("/%s", $ekey)));
$entry = new Entry(depth: $level, key: $key, node: $node);
$this->list[$entryKey] = $entry;
if ($node instanceof ArrayNode) {
$index = 0;
foreach ($node->items as $item) {
$this->parseNode($item, [ ...$path, $key ], $index++);
}
$this->list[$entryKey.'$'] = new Entry(depth: $level, key: $key, node: $node, closer: true);
} elseif ($node instanceof ObjectNode) {
foreach ($node->properties as $nodekey=>$item) {
$this->parseNode($item, [ ...$path, $key ], $nodekey);
}
$this->list[$entryKey.'$'] = new Entry(depth: $level, key: $key, node: $node, closer: true);
}
}
public function getPathForIndex(int $index): ?string
{
return array_keys($this->list)[$index]??null;
}
public function getIndexForPath(string $path): ?int
{
return array_search($path, array_keys($this->list));
}
public function getNodeForPath(string $path): ?Node
{
$entry = $this->list[$path]??null;
return $entry ? $entry->node : null;
}
public function getNodeForIndex(int $index): ?Node
{
$key = array_keys($this->list)[$index]??null;
$entry = $this->list[$key]??null;
if (!$entry) return null;
return $entry->node;
}
public function getEntryForIndex(int $index): ?Entry
{
$key = array_keys($this->list)[$index]??null;
$entry = $this->list[$key]??null;
if (!$entry) return null;
return $entry;
}
public function findNearestCollection(int $index, bool $ignoreSelf = false): ?int
{
$path = $this->getPathForIndex($index);
if ($ignoreSelf) {
$path = dirname($path);
}
while (strlen($path) > 0) {
$entry = $this->list[$path];
$node = $entry->node;
if ($node instanceof ArrayNode || $node instanceof ObjectNode) {
return $this->getIndexForPath($path);
}
$path = dirname($path);
}
// $depth = $this->getEntryForIndex($index)->depth;
// if ($ignoreSelf) $depth = max(0, $depth - 1);
// echo "{start={$depth}}"; sleep(1);
// while ($index >= 0) {
// $entry = $this->getEntryForIndex($index);
// if ($entry->depth < $depth) {
// $node = $entry->node;
// if ($node instanceof ArrayNode || $node instanceof ObjectNode) {
// return $index;
// }
// }
// $index--;
// }
return null;
}
public function drawEntry(int $screenRow, int $entryRow, int $columns, bool $selected): void
{
$keys = array_keys($this->list);
echo "\e[{$screenRow};1H";
if ($entryRow >= count($keys)) {
echo ($selected?"\e[100;97m":"\e[0;90m")."\e[K";
if ($entryRow == count($keys)) echo str_repeat("\u{2574}",$columns);
echo "\e[0m";
//else echo "\e[90m\u{2805}\e[0m";
return;
}
$key = $keys[$entryRow];
if (!isset($this->list[$key])) {
echo "\e[0m\e[K\e[31mE_NO_KEY_IN_LIST\e[0m";
return;
}
$entry = $this->list[$key];
if (!$entry) {
echo "\e[0m\e[K\e[31mE_NO_ENTRY_IN_LIST\e[0m";
return;
}
echo "\e[{$screenRow};1H\e[0m";
echo ($selected?"\e[100;97m":"\e[0;37m")."\e[K";
echo "\e[90m".str_repeat("\u{258f} ",$entry->depth)."\e[37m";
if ($entry->closer) {
if ($entry->node instanceof ArrayNode) {
echo "]";
} elseif ($entry->node instanceof ObjectNode) {
echo "}";
}
return;
}
if (!is_null($entry->key)) {
echo (is_int($entry->key)
?"\e[36;2m\u{e0b6}\e[7m#{$entry->key}\e[27m\u{e0b4}\e[22m "
:"\e[36m{$entry->key}:\e[37m ");
}
if ($entry->node instanceof ArrayNode) {
echo "[";
} elseif ($entry->node instanceof ObjectNode) {
echo "{";
} elseif ($entry->node instanceof ValueNode) {
$value = $entry->node->value;
echo match (gettype($value)) {
'string' => "\e[33m",
'integer' => "\e[34m",
'double' => "\e[32m",
'boolean' => "\e[35m",
'NULL' => "\e[31m",
default => "",
};
echo json_encode($entry->node->value, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
}
}
}