diff --git a/.gitignore b/.gitignore index 57872d0..5a6ec21 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /vendor/ +/*.phar diff --git a/composer.json b/composer.json index 9e30833..e3b56bf 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "noccylabs/jedit", - "description": "JSON/YAML editor for the terminal", + "description": "JSON/YAML editor for terminal junkies", "type": "application", "license": "GPL-3.0-or-later", "autoload": { @@ -20,4 +20,4 @@ "bin": [ "bin/jedit" ] -} \ No newline at end of file +} diff --git a/src/Editor/Editor.php b/src/Editor/Editor.php index 0055c34..eda6f66 100644 --- a/src/Editor/Editor.php +++ b/src/Editor/Editor.php @@ -204,15 +204,78 @@ class Editor break; case 'Q': - case "\x03": + case "\x03": // ctrl-w $this->running = false; break; + case "\x12": // ctrl-r + $readFrom = $this->ask("\e[33mRead from:\e[0m ", ""); + + $this->term->setCursor(1, $h); + if (!file_exists($readFrom)) { + echo "\e[97;41mFile does not exist\e[K\e[0m"; + break; + } + if (!is_readable($readFrom)) { + echo "\e[97;41mFile not readable\e[K\e[0m"; + break; + } + $ext = strtolower(pathinfo($readFrom, PATHINFO_EXTENSION)); + switch ($ext) { + case 'json': + $doc = json_decode(file_get_contents($readFrom)); + break; + case 'yml': + case 'yaml': + $doc = Yaml::parseFile($readFrom); + break; + default: + echo "\e[97;41mUnable to read format: {$ext}\e[K\e[0m"; + break(2); + } + + $this->filename = $readFrom; + $this->shortfilename = basename($readFrom); + + $this->document->load($doc); + $this->currentRow = 0; + $this->list->parseTree(); + $this->redrawEditor(); + + $this->term->setCursor(1, $h); + echo "\e[97;42mLoaded {$readFrom}\e[K\e[0m"; + break; + + + case "\x17": // ctrl-w + $saveTo = $this->ask("\e[33mWrite to:\e[0m ", $this->filename); + + $doc = $this->document->save(); + + $this->term->setCursor(1, $h); + + $ext = strtolower(pathinfo($saveTo, PATHINFO_EXTENSION)); + switch ($ext) { + case 'json': + file_put_contents($saveTo, json_encode($doc, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES)."\n"); + break; + case 'yml': + case 'yaml': + $doc = json_decode(json_encode($doc), true); + file_put_contents($saveTo, Yaml::dump($doc)); + break; + default: + echo "\e[97;41mUnable to write format: {$ext}\e[K\e[0m"; + } + + echo "\e[97;42mWrote to {$saveTo}\e[K\e[0m"; + break; + case null: break; default: $this->term->setCursor(1, $h, true); - echo $read; + echo sprintf("%s %02x %03d", ctype_print($read)?$read:'.', ord($read), ord($read)); sleep(1); } } @@ -248,10 +311,11 @@ class Editor return $value; } if (ord($ch) == 3) { + // ctrl-c $this->term->setCursor(1, $h); echo "\e[0m\e[K"; return null; - } + } } elseif (ord($ch) == 127) { if ($cursorPos > 0) { $value = substr($value, 0, $cursorPos - 1) . substr($value, $cursorPos); diff --git a/src/List/TreeList.php b/src/List/TreeList.php index 190bd14..c47ed71 100644 --- a/src/List/TreeList.php +++ b/src/List/TreeList.php @@ -154,7 +154,7 @@ class TreeList implements Countable 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 "); + :"\e[36m{$entry->key}:\e[37m "); } if ($entry->node instanceof ArrayNode) { echo "["; diff --git a/src/Terminal/Terminal.php b/src/Terminal/Terminal.php index e049313..e44249f 100644 --- a/src/Terminal/Terminal.php +++ b/src/Terminal/Terminal.php @@ -2,7 +2,6 @@ namespace NoccyLabs\JEdit\Terminal; - class Terminal { private static int $init = 0; diff --git a/src/Tree/ArrayNode.php b/src/Tree/ArrayNode.php index 7b93a90..2f5b324 100644 --- a/src/Tree/ArrayNode.php +++ b/src/Tree/ArrayNode.php @@ -27,5 +27,10 @@ class ArrayNode extends Node implements CollapsibleNode return new ArrayNode($items); } + public function jsonSerialize(): mixed + { + return array_values($this->items); + } + } diff --git a/src/Tree/Node.php b/src/Tree/Node.php index 0935daa..bd04968 100644 --- a/src/Tree/Node.php +++ b/src/Tree/Node.php @@ -2,7 +2,9 @@ namespace NoccyLabs\JEdit\Tree; -abstract class Node +use JsonSerializable; + +abstract class Node implements JsonSerializable { } diff --git a/src/Tree/ObjectNode.php b/src/Tree/ObjectNode.php index a4c8174..bfcd27c 100644 --- a/src/Tree/ObjectNode.php +++ b/src/Tree/ObjectNode.php @@ -29,5 +29,10 @@ class ObjectNode extends Node implements CollapsibleNode return new ObjectNode($properties); } + public function jsonSerialize(): mixed + { + return $this->properties; + } + } diff --git a/src/Tree/Tree.php b/src/Tree/Tree.php index 542c22f..af538c4 100644 --- a/src/Tree/Tree.php +++ b/src/Tree/Tree.php @@ -13,6 +13,18 @@ class Tree return $this; } + public function save(): mixed + { + if (!$this->root) + return null; + + return json_decode( + json_encode( + $this->root + ) + ); + } + private function parseNode(mixed $node): Node { if (is_array($node) && array_is_list($node)) { diff --git a/src/Tree/ValueNode.php b/src/Tree/ValueNode.php index 8f80528..bd0d82d 100644 --- a/src/Tree/ValueNode.php +++ b/src/Tree/ValueNode.php @@ -7,5 +7,10 @@ class ValueNode extends Node public function __construct(public mixed $value) { } + + public function jsonSerialize(): mixed + { + return $this->value; + } }