186 lines
		
	
	
		
			5.6 KiB
		
	
	
	
		
			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);
 | 
						|
        }
 | 
						|
        
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
 |