Improve tail line, fix edit box
* Edit box now supports the usual keys; left/right plus home/end, and does the appropriate scrolling. * Tail line is now a top bar. * Added option to toggle tail bar, but not bound to any key.
This commit is contained in:
parent
466406733d
commit
19ba6a9bee
@ -49,6 +49,8 @@ class Editor
|
|||||||
// ]);
|
// ]);
|
||||||
|
|
||||||
$this->list = new TreeList($this->document);
|
$this->list = new TreeList($this->document);
|
||||||
|
|
||||||
|
$this->setWindowTitle($this->shortfilename." - JSONEdit");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,6 +75,7 @@ class Editor
|
|||||||
default:
|
default:
|
||||||
throw new \RuntimeException("Unable to read file of type {$ext}");
|
throw new \RuntimeException("Unable to read file of type {$ext}");
|
||||||
}
|
}
|
||||||
|
$this->setWindowTitle($this->shortfilename." - JSONEdit");
|
||||||
$this->document->load($doc);
|
$this->document->load($doc);
|
||||||
$this->list->parseTree();
|
$this->list->parseTree();
|
||||||
}
|
}
|
||||||
@ -108,7 +111,7 @@ class Editor
|
|||||||
$this->redrawEditor();
|
$this->redrawEditor();
|
||||||
break;
|
break;
|
||||||
case 'k{DOWN}':
|
case 'k{DOWN}':
|
||||||
$this->currentRow = min(count($this->list) - 1, $this->currentRow + 1);
|
$this->currentRow = min(count($this->list), $this->currentRow + 1);
|
||||||
$this->redrawEditor();
|
$this->redrawEditor();
|
||||||
break;
|
break;
|
||||||
case 'k{PGUP}':
|
case 'k{PGUP}':
|
||||||
@ -260,13 +263,45 @@ class Editor
|
|||||||
$this->redrawEditor();
|
$this->redrawEditor();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "-":
|
||||||
|
foreach ($this->list as $path => $entry) {
|
||||||
|
$node = $entry->node;
|
||||||
|
if ($path == "/") {
|
||||||
|
if ($node instanceof CollapsibleNode) {
|
||||||
|
$node->collapse(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($node instanceof CollapsibleNode) {
|
||||||
|
$node->collapse(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->currentRow = 0;
|
||||||
|
$this->list->parseTree();
|
||||||
|
$this->redrawEditor();
|
||||||
|
break;
|
||||||
|
|
||||||
case "k{LEFT}":
|
case "k{LEFT}":
|
||||||
$node = $this->list->getNodeForIndex($this->currentRow);
|
$node = $this->list->getNodeForIndex($this->currentRow);
|
||||||
if ($node instanceof CollapsibleNode) {
|
if ($node instanceof CollapsibleNode) {
|
||||||
|
if ($node->isCollapsed()) {
|
||||||
|
$path = $this->list->getPathForIndex($this->currentRow);
|
||||||
|
$parent = $this->list->getIndexForPath(dirname($path));
|
||||||
|
if ($parent) {
|
||||||
|
$this->currentRow = $parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
$node->collapse(true);
|
$node->collapse(true);
|
||||||
$this->list->parseTree();
|
$this->list->parseTree();
|
||||||
$this->redrawEditor();
|
$this->redrawEditor();
|
||||||
|
} else {
|
||||||
|
$path = $this->list->getPathForIndex($this->currentRow);
|
||||||
|
$parent = $this->list->getIndexForPath(dirname($path));
|
||||||
|
if ($parent) {
|
||||||
|
$this->currentRow = $parent;
|
||||||
|
}
|
||||||
|
$this->redrawEditor();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -373,6 +408,8 @@ class Editor
|
|||||||
$this->list->parseTree();
|
$this->list->parseTree();
|
||||||
$this->redrawEditor();
|
$this->redrawEditor();
|
||||||
|
|
||||||
|
$this->setWindowTitle($this->shortfilename." - JSONEdit");
|
||||||
|
|
||||||
$this->showMessage("\e[97;42mLoaded {$readFrom}");
|
$this->showMessage("\e[97;42mLoaded {$readFrom}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,6 +450,8 @@ class Editor
|
|||||||
$this->modified = false;
|
$this->modified = false;
|
||||||
$this->redrawEditor();
|
$this->redrawEditor();
|
||||||
|
|
||||||
|
$this->setWindowTitle($this->shortfilename." - JSONEdit");
|
||||||
|
|
||||||
$this->showMessage("\e[97;42mWrote to {$saveTo}");
|
$this->showMessage("\e[97;42mWrote to {$saveTo}");
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -469,14 +508,11 @@ class Editor
|
|||||||
This is beta software, if not alpha. It kinda works, but there will be issues. Feel free to help out with a patch, or by filing bug reports.
|
This is beta software, if not alpha. It kinda works, but there will be issues. Feel free to help out with a patch, or by filing bug reports.
|
||||||
Known issues include:
|
Known issues include:
|
||||||
|
|
||||||
* Editing long lines will blow up. Don't try to edit anything longer than the terminal is wide.
|
|
||||||
* There is no fullscreen editing, so verbatim blocks in twig will probably not work well either.
|
|
||||||
* Comments are not preserved.
|
* Comments are not preserved.
|
||||||
* Files are overwritten without confirmation.
|
* Files are overwritten without confirmation.
|
||||||
* There is no command mode, no search.
|
* There is no command mode, no search.
|
||||||
* Some things just don't work yet.
|
* Some things just don't work yet.
|
||||||
* Unhandled keys will appear in the bottom left of the screen with a delay.
|
* Unhandled keys will appear in the bottom left of the screen with a delay.
|
||||||
* Folding is not yet implemented.
|
|
||||||
* There are crashes, and lock-ups. Data corruption is a possibility.
|
* There are crashes, and lock-ups. Data corruption is a possibility.
|
||||||
|
|
||||||
# Support
|
# Support
|
||||||
@ -643,12 +679,22 @@ class Editor
|
|||||||
$promptLen = mb_strlen($plainPrompt);
|
$promptLen = mb_strlen($plainPrompt);
|
||||||
[$w,$h] = $this->term->getSize();
|
[$w,$h] = $this->term->getSize();
|
||||||
|
|
||||||
|
$available = $w - $promptLen - 1;
|
||||||
|
|
||||||
$prompting = true;
|
$prompting = true;
|
||||||
$cursorPos = mb_strlen($value);
|
$cursorPos = mb_strlen($value);
|
||||||
|
$scrollPos = max(0, $cursorPos - $available);
|
||||||
while ($prompting) {
|
while ($prompting) {
|
||||||
|
while (($cursorPos - $scrollPos) > $available) {
|
||||||
|
$scrollPos++;
|
||||||
|
}
|
||||||
|
while (($cursorPos - $scrollPos) < 0) {
|
||||||
|
$scrollPos--;
|
||||||
|
}
|
||||||
|
$cursorOffs = $cursorPos - $scrollPos;
|
||||||
$this->term->setCursor(1, $h);
|
$this->term->setCursor(1, $h);
|
||||||
echo $prompt."\e[0m\e[K".$value;
|
echo $prompt."\e[0m\e[K".mb_substr($value, $scrollPos, $available);
|
||||||
$this->term->setCursor($promptLen+$cursorPos+1, $h, true);
|
$this->term->setCursor($promptLen+$cursorOffs+1, $h, true);
|
||||||
while (null === ($ch = $this->term->readKey())) {
|
while (null === ($ch = $this->term->readKey())) {
|
||||||
usleep(10000);
|
usleep(10000);
|
||||||
}
|
}
|
||||||
@ -684,6 +730,12 @@ class Editor
|
|||||||
if ($cursorPos < mb_strlen($value))
|
if ($cursorPos < mb_strlen($value))
|
||||||
$cursorPos++;
|
$cursorPos++;
|
||||||
break;
|
break;
|
||||||
|
case "k{HOME}":
|
||||||
|
$cursorPos = 0;
|
||||||
|
break;
|
||||||
|
case "k{END}":
|
||||||
|
$cursorPos = mb_strlen($value);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -699,9 +751,16 @@ class Editor
|
|||||||
{
|
{
|
||||||
[$w,$h] = $this->term->getSize();
|
[$w,$h] = $this->term->getSize();
|
||||||
|
|
||||||
|
// Jump to the tail line if the cursor is past the end of the entry list
|
||||||
|
if ($this->currentRow > count($this->list)) $this->currentRow = count($this->list);
|
||||||
|
|
||||||
|
// Make sure the selection is in view
|
||||||
while ($this->currentRow < $this->scrollOffset) $this->scrollOffset--;
|
while ($this->currentRow < $this->scrollOffset) $this->scrollOffset--;
|
||||||
while ($this->currentRow > $h + $this->scrollOffset - 3) $this->scrollOffset++;
|
while ($this->currentRow > $h + $this->scrollOffset - 3) $this->scrollOffset++;
|
||||||
|
|
||||||
|
// Nudge back so the tail line is visible but not selected
|
||||||
|
if ($this->currentRow == count($this->list)) $this->currentRow--;
|
||||||
|
|
||||||
$path = $this->list->getPathForIndex($this->currentRow);
|
$path = $this->list->getPathForIndex($this->currentRow);
|
||||||
$node = $this->list->getNodeForIndex($this->currentRow);
|
$node = $this->list->getNodeForIndex($this->currentRow);
|
||||||
|
|
||||||
@ -770,9 +829,10 @@ class Editor
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->term->setCursor(1, $h);
|
$this->term->setCursor(1, $h);
|
||||||
echo "\e[0;40m\e[K";
|
echo "\e[37;40m\e[K";
|
||||||
foreach ($keys as $key=>$info)
|
foreach ($keys as $key=>$info)
|
||||||
echo "\e[37;40;2m\u{e0b6}\e[7;37m{$key} \e[22m {$info} \e[27m\u{e0b4}\e[0m";
|
echo "\e[2m\u{f104}\e[22;97;1m{$key}\e[22;37;2m\u{f105} \e[22;36m{$info}\e[37m ";
|
||||||
|
//echo "\e[37;40;2m\u{e0b6}\e[7;37m{$key} \e[22m {$info} \e[27m\u{e0b4}\e[0m";
|
||||||
//echo " \e[1m{$key}\e[2m \e[3m{$info}\e[0m \e[90m\u{2502}\e[0m";
|
//echo " \e[1m{$key}\e[2m \e[3m{$info}\e[0m \e[90m\u{2502}\e[0m";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -784,4 +844,9 @@ class Editor
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function setWindowTitle(string $title): void
|
||||||
|
{
|
||||||
|
echo "\e]2;{$title}\x07";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace NoccyLabs\JsonEdit\List;
|
namespace NoccyLabs\JsonEdit\List;
|
||||||
|
|
||||||
|
use ArrayIterator;
|
||||||
use Countable;
|
use Countable;
|
||||||
|
use IteratorAggregate;
|
||||||
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\CollapsibleNode;
|
||||||
@ -10,8 +12,9 @@ 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;
|
||||||
use NoccyLabs\JsonEdit\Tree\ValueNode;
|
use NoccyLabs\JsonEdit\Tree\ValueNode;
|
||||||
|
use Traversable;
|
||||||
|
|
||||||
class TreeList implements Countable
|
class TreeList implements Countable, IteratorAggregate
|
||||||
{
|
{
|
||||||
/** @var array<string,Entry> */
|
/** @var array<string,Entry> */
|
||||||
public array $list = [];
|
public array $list = [];
|
||||||
@ -31,6 +34,11 @@ class TreeList implements Countable
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getIterator(): Traversable
|
||||||
|
{
|
||||||
|
return new ArrayIterator($this->list);
|
||||||
|
}
|
||||||
|
|
||||||
public function count(): int
|
public function count(): int
|
||||||
{
|
{
|
||||||
return count($this->list);
|
return count($this->list);
|
||||||
@ -146,7 +154,7 @@ class TreeList implements Countable
|
|||||||
echo "\e[{$screenRow};1H";
|
echo "\e[{$screenRow};1H";
|
||||||
if ($entryRow >= count($keys)) {
|
if ($entryRow >= count($keys)) {
|
||||||
echo ($selected?"\e[44;97m":"\e[0;90m")."\e[K";
|
echo ($selected?"\e[44;97m":"\e[0;90m")."\e[K";
|
||||||
if ($entryRow == count($keys)) echo str_repeat("\u{2574}",$columns);
|
if ($entryRow == count($keys) && Settings::$tailLine) echo str_repeat("\u{2594}",$columns);
|
||||||
echo "\e[0m";
|
echo "\e[0m";
|
||||||
//else echo "\e[90m\u{2805}\e[0m";
|
//else echo "\e[90m\u{2805}\e[0m";
|
||||||
return;
|
return;
|
||||||
@ -183,10 +191,10 @@ class TreeList implements Countable
|
|||||||
if ($entry->node instanceof CollapsibleNode) {
|
if ($entry->node instanceof CollapsibleNode) {
|
||||||
if ($entry->node->isCollapsed()) {
|
if ($entry->node->isCollapsed()) {
|
||||||
//echo "\e[90m\u{25ba} \e[37m";
|
//echo "\e[90m\u{25ba} \e[37m";
|
||||||
echo "\e[90m".self::TREE_FOLD_OPEN." \e[37m";
|
echo "\e[37m".self::TREE_FOLD_OPEN." \e[37m";
|
||||||
} else {
|
} else {
|
||||||
//echo "\e[90m\u{25bc} \e[37m";
|
//echo "\e[90m\u{25bc} \e[37m";
|
||||||
echo "\e[90m".self::TREE_FOLD_CLOSE." \e[37m";
|
echo "\e[37m".self::TREE_FOLD_CLOSE." \e[37m";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,8 +239,8 @@ class TreeList implements Countable
|
|||||||
$value = $entry->node->value;
|
$value = $entry->node->value;
|
||||||
echo match (gettype($value)) {
|
echo match (gettype($value)) {
|
||||||
'string' => "\e[33m",
|
'string' => "\e[33m",
|
||||||
'integer' => "\e[34m",
|
'integer' => "\e[94m",
|
||||||
'double' => "\e[32m",
|
'double' => "\e[96m",
|
||||||
'boolean' => "\e[35m",
|
'boolean' => "\e[35m",
|
||||||
'NULL' => "\e[31m",
|
'NULL' => "\e[31m",
|
||||||
default => "",
|
default => "",
|
||||||
|
@ -14,6 +14,8 @@ class Settings
|
|||||||
|
|
||||||
public static bool $highlightRow = true;
|
public static bool $highlightRow = true;
|
||||||
|
|
||||||
|
public static bool $tailLine = 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)) {
|
||||||
|
@ -115,6 +115,12 @@ class Terminal
|
|||||||
} elseif (strncmp($this->inputBuffer, "\e[D", 3) === 0) {
|
} elseif (strncmp($this->inputBuffer, "\e[D", 3) === 0) {
|
||||||
$this->inputBuffer = mb_substr($this->inputBuffer, 3);
|
$this->inputBuffer = mb_substr($this->inputBuffer, 3);
|
||||||
return "k{LEFT}";
|
return "k{LEFT}";
|
||||||
|
} elseif (strncmp($this->inputBuffer, "\e[H", 3) === 0) {
|
||||||
|
$this->inputBuffer = mb_substr($this->inputBuffer, 3);
|
||||||
|
return "k{HOME}";
|
||||||
|
} elseif (strncmp($this->inputBuffer, "\e[F", 3) === 0) {
|
||||||
|
$this->inputBuffer = mb_substr($this->inputBuffer, 3);
|
||||||
|
return "k{END}";
|
||||||
} elseif (strncmp($this->inputBuffer, "\e[5~", 4) === 0) {
|
} elseif (strncmp($this->inputBuffer, "\e[5~", 4) === 0) {
|
||||||
$this->inputBuffer = mb_substr($this->inputBuffer, 4);
|
$this->inputBuffer = mb_substr($this->inputBuffer, 4);
|
||||||
return "k{PGUP}";
|
return "k{PGUP}";
|
||||||
|
Loading…
Reference in New Issue
Block a user