diff --git a/src/Editor/Editor.php b/src/Editor/Editor.php index 9ef665d..fef1525 100644 --- a/src/Editor/Editor.php +++ b/src/Editor/Editor.php @@ -3,6 +3,7 @@ namespace NoccyLabs\JEdit\Editor; use NoccyLabs\JEdit\List\TreeList; +use NoccyLabs\JEdit\Settings; use NoccyLabs\JEdit\Terminal\Terminal; use NoccyLabs\JEdit\Tree\ArrayNode; use NoccyLabs\JEdit\Tree\ObjectNode; @@ -27,6 +28,8 @@ class Editor private bool $running = true; + private bool $modified = false; + /** * Constructor * @@ -137,10 +140,12 @@ class Editor $collNode->removeIndex($deleteKey); $this->list->parseTree(); $this->redrawEditor(); + $this->modified = true; } elseif ($collNode instanceof ObjectNode) { $collNode->unset($deleteKey); $this->list->parseTree(); $this->redrawEditor(); + $this->modified = true; } else { $this->term->setCursor(1, $h); echo "\e[97;41mCan only delete from object, array\e[K\e[0m"; @@ -175,18 +180,58 @@ class Editor switch ($sel) { case 'value': $this->doInsertValue(); + $this->modified = true; break; case 'array': $this->doInsertValue('[]'); + $this->modified = true; break; case 'object': $this->doInsertValue('{}'); + $this->modified = true; break; } break; + case 'h': + $text = <<term, $text, "Help (press ctrl-C to close)"); + $msg->display(5, 3, 70, 20); + $this->redrawEditor(); + break; + case 'i': $this->doInsertValue(); + $this->modified = true; break; case 'Q': @@ -194,6 +239,20 @@ class Editor $this->running = false; break; + case "q": + Settings::$editorQuotedKeys = !Settings::$editorQuotedKeys; + $this->redrawEditor(); + break; + + case "\x0e": // ctrl-n + $this->document->load((object)[]); + $this->list->parseTree(); + $this->filename = "untitled.json"; + $this->shortfilename = "untitled.json"; + $this->modified = false; + $this->redrawEditor(); + break; + case "\x12": // ctrl-r $this->doReadFile(); break; @@ -211,6 +270,8 @@ class Editor } } + Settings::save(SETTINGS_FILE); + // for ($n = 0; $n < 20; $n++) { // $this->currentRow = $n; // $this->redrawEditor(); @@ -278,7 +339,6 @@ class Editor $doc = $this->document->save(); - $this->term->setCursor(1, $h); $ext = strtolower(pathinfo($saveTo, PATHINFO_EXTENSION)); switch ($ext) { @@ -291,11 +351,19 @@ class Editor file_put_contents($saveTo, Yaml::dump($doc)); break; default: + $this->term->setCursor(1, $h); echo "\e[97;41mUnable to write format: {$ext}\e[K\e[0m"; } + $this->filename = $saveTo; + $this->shortfilename = basename($saveTo); + $this->modified = false; + $this->redrawEditor(); + + $this->term->setCursor(1, $h); echo "\e[97;42mWrote to {$saveTo}\e[K\e[0m"; + } /** @@ -316,7 +384,9 @@ class Editor if ($parent->node instanceof ObjectNode) { $newVal = $this->ask("\e[0;33mnew key:\e[0m ", $entry->key); if ($newVal !== null) { + $parent->node->rename($entry->key, $newVal); $entry->key = $newVal; + $this->list->parseTree(); $this->redrawEditor(); } } else { @@ -347,6 +417,7 @@ class Editor $val = $newVal; } $node->value = $val; + $this->modified = true; $this->redrawEditor(); } else { $this->redrawInfoBar(); @@ -395,6 +466,7 @@ class Editor } elseif ($node instanceof ObjectNode) { $node->set($key, $valueNode); } + $this->modified = true; $this->list->parseTree(); $this->redrawEditor(); } else { @@ -477,8 +549,9 @@ class Editor $path = $this->list->getPathForIndex($this->currentRow); $node = $this->list->getNodeForIndex($this->currentRow); + $modified = $this->modified ? "\e[31m*\e[37m" : ""; $this->term->setCursor(1, $h-1); - echo "\e[40;37m\e[K\e[1m{$this->shortfilename}\e[22m#\e[3m{$path}\e[37;23m"; + echo "\e[40;37m\e[K{$modified}\e[1m{$this->shortfilename}\e[22m#\e[3m{$path}\e[37;23m"; //$this->term->setCursor(1, $h); echo " = "; diff --git a/src/Editor/MessageBox.php b/src/Editor/MessageBox.php new file mode 100644 index 0000000..74704e6 --- /dev/null +++ b/src/Editor/MessageBox.php @@ -0,0 +1,110 @@ +text, $width - 4, cut_long_words:true)); + + if ($height == 0) $height = count($wrapped) + 4; + + $maxScroll = (count($wrapped) > $height) ? count($wrapped) - $height + 4 : 0; + + $this->redraw($left, $top, $width, $height, $wrapped, $this->title, $maxScroll); + + $showing = true; + while ($showing) { + while (null === ($ch = $this->terminal->readKey())) { + usleep(10000); + } + if (mb_strlen($ch) == 1) { + if ($ch == "\x03") { + $showing = false; + } + if ($ch == "\r") { + $showing = false; + } + } else { + if ($ch == "k{UP}") { + $this->scroll--; + if ($this->scroll < 0) $this->scroll = 0; + $this->redraw($left, $top, $width, $height, $wrapped, $this->title, $maxScroll); + } elseif ($ch == "k{DOWN}") { + $this->scroll++; + if ($this->scroll > $maxScroll) $this->scroll = $maxScroll; + $this->redraw($left, $top, $width, $height, $wrapped, $this->title, $maxScroll); + } + } + } + + + return; + } + + private function redraw(int $left, int $top, int $width, int $height, array $wrapped, string $title, int $maxScroll) { + $visibleItems = $height - 4; + + // calculate scrollbar thumb positions + $scrollTop = ($this->scroll / ($maxScroll + $visibleItems)); + $scrollVisible = $visibleItems / count($wrapped); // / $visibleItems; + $scrollBottom = $scrollTop + $scrollVisible; + $thumbTop = round($scrollTop * $visibleItems); + $thumbBottom = round($visibleItems * $scrollBottom); + + echo "\e[40;37m"; + $this->terminal + ->writeAt($left, $top + 0, self::U_CORNER_ROUNDED_TOPLEFT.str_repeat(self::U_EDGE_HORIZONTAL,$width - 2).self::U_CORNER_ROUNDED_TOPRIGHT) + ->writeAt($left, $top + 1, self::U_EDGE_VERTICAL.str_repeat(" ",$width - 2).self::U_EDGE_VERTICAL) + ->writeAt($left + 2, $top + 1, "\e[1m" . $this->title . "\e[22m") + ->writeAt($left, $top + 2, self::U_EDGE_VERTICAL_RIGHT.str_repeat(self::U_EDGE_HORIZONTAL,$width - 2).self::U_EDGE_VERTICAL_LEFT) + ->writeAt($left, $top + $height - 1, self::U_CORNER_BOTTOMLEFT.str_repeat(self::U_EDGE_HORIZONTAL,$width - 2).self::U_CORNER_BOTTOMRIGHT) + ; + for ($n = 0; $n < $visibleItems; $n++) { + if (isset($wrapped[$n+$this->scroll])) { + $item = " " . $wrapped[$n+$this->scroll]??null; + $item = $item . str_repeat(" ", $width - 2 - mb_strlen($item)) . "\e[40;37m"; + } else { + $item = str_repeat(" ", $width - 2)."\e[40;37m"; + } + if ($n >= $thumbTop && $n <= $thumbBottom) { + $scrollbar = "\e[97;1m".self::U_EDGE_VERTICAL_THUMB."\e[40;37;22m"; + } else { + $scrollbar = "\e[37;2m".self::U_EDGE_VERTICAL_SCROLL."\e[40;37;22m"; + } + $this->terminal + ->writeAt($left, $top + 3 + $n, self::U_EDGE_VERTICAL.$item.$scrollbar); + } + + echo "\e[0m"; + } + +} \ No newline at end of file diff --git a/src/List/TreeList.php b/src/List/TreeList.php index d2524d4..d9c8c44 100644 --- a/src/List/TreeList.php +++ b/src/List/TreeList.php @@ -3,6 +3,7 @@ namespace NoccyLabs\JEdit\List; use Countable; +use NoccyLabs\JEdit\Settings; use NoccyLabs\JEdit\Tree\ArrayNode; use NoccyLabs\JEdit\Tree\Tree; use NoccyLabs\JEdit\Tree\Node; @@ -159,8 +160,11 @@ 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[36;2m\u{e0b6}\e[7m#{$entry->key}\e[27m\u{e0b4}\e[22;37m " + :(Settings::$editorQuotedKeys + ? "\e[36m\"{$entry->key}\":\e[37m " + : "\e[36m{$entry->key}:\e[37m " + )); } if ($entry->node instanceof ArrayNode) { echo "["; diff --git a/src/Settings.php b/src/Settings.php new file mode 100644 index 0000000..a5483f0 --- /dev/null +++ b/src/Settings.php @@ -0,0 +1,32 @@ +editorQuotedKeys ?? false; + } + } + + public static function save(string $filename): void + { + $data = json_encode([ + 'editorQuotedKeys' => self::$editorQuotedKeys + ], JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT)."\n"; + @file_put_contents($filename, $data); + } + + public static function set(string $key, mixed $value): void + { + switch ($key) { + case 'editorQuotedKeys': + self::$editorQuotedKeys = (bool)$value; break; + } + } +} \ No newline at end of file diff --git a/src/Tree/ObjectNode.php b/src/Tree/ObjectNode.php index bfcd27c..7241d69 100644 --- a/src/Tree/ObjectNode.php +++ b/src/Tree/ObjectNode.php @@ -15,6 +15,15 @@ class ObjectNode extends Node implements CollapsibleNode $this->properties[$key] = $value; } + public function rename(string $key, string $newKey) + { + $renamed = []; + foreach ($this->properties as $k=>$v) { + $renamed[($k==$key)?$newKey:$k] = $v; + } + $this->properties = $renamed; + } + public function unset(string $key) { unset($this->properties[$key]); diff --git a/src/entry.php b/src/entry.php index 8ca77a8..563665c 100644 --- a/src/entry.php +++ b/src/entry.php @@ -1,11 +1,17 @@ loadFile($filename);