Editor fixes, code cleanup, bool colors, open dialog
This commit is contained in:
		@@ -10,11 +10,14 @@ use NoccyLabs\JsonEdit\Tree\CollapsibleNode;
 | 
			
		||||
use NoccyLabs\JsonEdit\Tree\ObjectNode;
 | 
			
		||||
use NoccyLabs\JsonEdit\Tree\Tree;
 | 
			
		||||
use NoccyLabs\JsonEdit\Tree\ValueNode;
 | 
			
		||||
use Symfony\Component\Yaml\Exception\ParseException;
 | 
			
		||||
use Symfony\Component\Yaml\Yaml;
 | 
			
		||||
 | 
			
		||||
class Editor
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public static string $helpText;
 | 
			
		||||
 | 
			
		||||
    private Tree $document;
 | 
			
		||||
 | 
			
		||||
    private TreeList $list;
 | 
			
		||||
@@ -201,7 +204,6 @@ class Editor
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case "\x18": // ctrl-x
 | 
			
		||||
 | 
			
		||||
                    if ($this->modified) {
 | 
			
		||||
                        $menu = new Menu($this->term, [
 | 
			
		||||
                            'cancel' => "Return to editor",
 | 
			
		||||
@@ -321,12 +323,28 @@ class Editor
 | 
			
		||||
                    $this->showMessage("Toggle compact groups (c)");
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case "\x0d": // enter
 | 
			
		||||
                    $node = $this->list->getNodeForIndex($this->currentRow);
 | 
			
		||||
                    if ($node instanceof ValueNode) {
 | 
			
		||||
                        if ($node->value === true) {
 | 
			
		||||
                            $node->value = false;
 | 
			
		||||
                            $this->modified = true;
 | 
			
		||||
                            $this->redrawEditor();
 | 
			
		||||
                        } elseif ($node->value === false) {
 | 
			
		||||
                            $node->value = true;
 | 
			
		||||
                            $this->modified = true;
 | 
			
		||||
                            $this->redrawEditor();
 | 
			
		||||
                        } else {
 | 
			
		||||
                            $this->doEditValue();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case "\x03": // ctrl-c
 | 
			
		||||
                    $this->showMessage("\e[30;43mPress Ctrl-X to exit.");
 | 
			
		||||
                    break;
 | 
			
		||||
    
 | 
			
		||||
                case "\x0e": // ctrl-n
 | 
			
		||||
 | 
			
		||||
                    if ($this->modified) {
 | 
			
		||||
                        $menu = new Menu($this->term, [
 | 
			
		||||
                            'cancel' => "Return to editor",
 | 
			
		||||
@@ -349,6 +367,8 @@ class Editor
 | 
			
		||||
                            case 'discard':
 | 
			
		||||
                                break;
 | 
			
		||||
                        }
 | 
			
		||||
                        $this->modified = false;
 | 
			
		||||
                        
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    $this->document->load((object)[]);
 | 
			
		||||
@@ -359,6 +379,10 @@ class Editor
 | 
			
		||||
                    $this->redrawEditor();
 | 
			
		||||
                    break;
 | 
			
		||||
    
 | 
			
		||||
                case "\x0F": // ctrl-o
 | 
			
		||||
                    $this->doOpenFile();
 | 
			
		||||
                    break;
 | 
			
		||||
 | 
			
		||||
                case "\x12": // ctrl-r
 | 
			
		||||
                    $this->doReadFile();
 | 
			
		||||
                    break;
 | 
			
		||||
@@ -369,6 +393,7 @@ class Editor
 | 
			
		||||
 | 
			
		||||
                case null:
 | 
			
		||||
                    break;
 | 
			
		||||
                
 | 
			
		||||
                default:
 | 
			
		||||
                    $this->term->setCursor(1, $h, true);
 | 
			
		||||
                    echo sprintf("%s %02x %03d", ctype_print($read)?$read:'.', ord($read), ord($read));
 | 
			
		||||
@@ -476,75 +501,68 @@ class Editor
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function doOpenFile()
 | 
			
		||||
    {
 | 
			
		||||
        $wd = getcwd();
 | 
			
		||||
        while (true) {
 | 
			
		||||
            $items = [
 | 
			
		||||
                dirname($wd) => sprintf(
 | 
			
		||||
                    "%-30s %10s %20s",
 | 
			
		||||
                    "Parent Directory",
 | 
			
		||||
                    "<DIR>",
 | 
			
		||||
                    "-"
 | 
			
		||||
                )
 | 
			
		||||
            ];
 | 
			
		||||
            $files = glob($wd."/*");
 | 
			
		||||
            foreach ($files as $file) {
 | 
			
		||||
                $items[$file] = sprintf(
 | 
			
		||||
                    "%-30s %10s %20s",
 | 
			
		||||
                    is_dir($file) ? ("<".basename($file).">") : (basename($file)),
 | 
			
		||||
                    is_dir($file) ? "<DIR>" : filesize($file),
 | 
			
		||||
                    date("Y-m-d H:i:s", filemtime($file))
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
            $menu = new Menu($this->term, $items, $wd);
 | 
			
		||||
            $sel = $menu->display(0, 0, 70, 20, null);
 | 
			
		||||
            if ($sel === null) {
 | 
			
		||||
                $this->redrawEditor();
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            if (is_dir($sel)) {
 | 
			
		||||
                $wd = $sel;
 | 
			
		||||
            }
 | 
			
		||||
            if (is_file($sel)) {
 | 
			
		||||
                $body = file_get_contents($sel);
 | 
			
		||||
                $json = json_decode($body);
 | 
			
		||||
                if ($json !== null) {
 | 
			
		||||
                    $this->loadDocument($json);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                try {
 | 
			
		||||
                    $yaml = Yaml::parse($body);
 | 
			
		||||
                    $this->loadDocument($yaml);
 | 
			
		||||
                    break;
 | 
			
		||||
                } catch (ParseException $e) {
 | 
			
		||||
                    $this->showMessage("\e[41;93mUnsupported file format");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        $this->filename = $sel;
 | 
			
		||||
        $this->shortfilename = basename($sel);
 | 
			
		||||
        $this->redrawEditor();
 | 
			
		||||
        $this->showMessage("\e[42;97mOpened {$this->shortfilename}");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private function doShowHelp(): void
 | 
			
		||||
    {
 | 
			
		||||
        [$w,$h] = $this->term->getSize();
 | 
			
		||||
 | 
			
		||||
        $text = <<<EOT
 | 
			
		||||
        Welcome to JSONEdit! The editor you have missed all this time without even knowing it!
 | 
			
		||||
 | 
			
		||||
        # QuickStart
 | 
			
		||||
 | 
			
		||||
        To get started, press I (shift-i) and add something to the document. Use the arrow keys to get around. To cancel a prompt, close a menu or close a dialog, press ctrl-C. When you are happy with your work, press ctrl-W and enter a filename to write. You can also press ctrl-R and read in a new file, overwriting your masterpiece.
 | 
			
		||||
 | 
			
		||||
        ## Useful keys
 | 
			
		||||
 | 
			
		||||
           ↑↓  Navigate values in document
 | 
			
		||||
            h  Show this help
 | 
			
		||||
            H  Show information about the app and license
 | 
			
		||||
            i  Insert a new value
 | 
			
		||||
            I  Insert value, array or object
 | 
			
		||||
            e  Edit selected value
 | 
			
		||||
            E  Edit selected key
 | 
			
		||||
            D  Delete selected key
 | 
			
		||||
            c  Toggle compact list view
 | 
			
		||||
            q  Toggle quoted keys in list view
 | 
			
		||||
           ^W  Write to file
 | 
			
		||||
           ^R  Read from file
 | 
			
		||||
           ^N  New document with empty object
 | 
			
		||||
           ^C  Cancel/Exit
 | 
			
		||||
        
 | 
			
		||||
        ## Doing stuff
 | 
			
		||||
 | 
			
		||||
        ### Adding keys or values
 | 
			
		||||
        To add a key or a value, navigate to a value in an array or object, or to a specific array or object, and press "i". You will be prompted for the value, and for objects the key.
 | 
			
		||||
        You can also press "I" to add arrays and objects. Just select what you want to add in the menu and press enter.
 | 
			
		||||
 | 
			
		||||
        ### Editing keys
 | 
			
		||||
        You can edit keys on objects. For this, press "E".
 | 
			
		||||
 | 
			
		||||
        ### Editing values
 | 
			
		||||
        To edit a value, press "e". The value is verbatim JSON, so strings should be quoted and all that. Anything that is unparsable JSON will be used as is, resulting in a string.
 | 
			
		||||
 | 
			
		||||
        ### Loading and Saving files
 | 
			
		||||
        To load a file, press ^R and enter the filename to read. To write to a file, press ^W and enter the filename to write to.
 | 
			
		||||
 | 
			
		||||
        ### YAML or JSON?
 | 
			
		||||
        There is no need to select YAML or JSON mode. All operations work the same, and the format is determined on load or save.
 | 
			
		||||
 | 
			
		||||
        # Disclaimer
 | 
			
		||||
 | 
			
		||||
        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:
 | 
			
		||||
 | 
			
		||||
        * Comments are not preserved.
 | 
			
		||||
        * Files are overwritten without confirmation.
 | 
			
		||||
        * There is no command mode, no search.
 | 
			
		||||
        * Some things just don't work yet.
 | 
			
		||||
        * Unhandled keys will appear in the bottom left of the screen with a delay.
 | 
			
		||||
        * There are crashes, and lock-ups. Data corruption is a possibility.
 | 
			
		||||
 | 
			
		||||
        # Support
 | 
			
		||||
 | 
			
		||||
        Go to https://dev.noccylabs.info/noccy/jsonedit to find the source code, issue tracker, and learn more about the project!
 | 
			
		||||
        EOT;
 | 
			
		||||
 | 
			
		||||
        $width = min(90, $w - 10);
 | 
			
		||||
        $height = min(40, $h - 6);
 | 
			
		||||
        $left = round(($w / 2) - ($width / 2));
 | 
			
		||||
        $top = round(($h / 2) - ($height / 2));
 | 
			
		||||
 | 
			
		||||
        $msg = new MessageBox($this->term, $text, "Help");
 | 
			
		||||
        $msg = new MessageBox($this->term, self::$helpText, "Help");
 | 
			
		||||
        $this->redrawInfoBar([ '↑/↓' => 'Scroll', '^C' => 'Close' ]);
 | 
			
		||||
        $msg->display($left, $top, $width, $height);
 | 
			
		||||
        $this->redrawEditor();
 | 
			
		||||
@@ -622,7 +640,14 @@ class Editor
 | 
			
		||||
        if ($node instanceof ValueNode) {
 | 
			
		||||
            $val = json_encode($node->value, JSON_UNESCAPED_SLASHES);
 | 
			
		||||
            $newVal = $this->ask("\e[33;1mnew value:\e[0m ", $val);
 | 
			
		||||
            // We get null on ctrl-C, so non-null is input
 | 
			
		||||
            if ($newVal !== null) {
 | 
			
		||||
                // Break out if the value has not changed
 | 
			
		||||
                if ($newVal === $val) {
 | 
			
		||||
                    $this->redrawInfoBar();
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                // Attempt to decode the value
 | 
			
		||||
                $val = json_decode($newVal);
 | 
			
		||||
                // If the string decodes to null, but isn't 'null', treat it as a string
 | 
			
		||||
                if ($val === null && $newVal !== 'null') {
 | 
			
		||||
@@ -815,7 +840,9 @@ class Editor
 | 
			
		||||
 | 
			
		||||
        $path = $this->list->getPathForIndex($this->currentRow);
 | 
			
		||||
        $node = $this->list->getNodeForIndex($this->currentRow);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        ob_start();
 | 
			
		||||
 | 
			
		||||
        $modified = $this->modified ? "\e[31m*\e[37m" : "";
 | 
			
		||||
        $this->term->setCursor(1, $h-1);
 | 
			
		||||
        echo "\e[40;37m\e[K{$modified}\e[1m{$this->shortfilename}\e[22m#\e[3m{$path}\e[37;23m";
 | 
			
		||||
@@ -841,6 +868,8 @@ class Editor
 | 
			
		||||
        }
 | 
			
		||||
        // $this->term->setCursor(1, 1);
 | 
			
		||||
        // echo "\e[90;3m«Empty»\e[0m";
 | 
			
		||||
 | 
			
		||||
        ob_end_flush();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -902,3 +931,63 @@ class Editor
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Editor::$helpText = <<<EOT
 | 
			
		||||
Welcome to JSONEdit! The editor you never knew you were missing!
 | 
			
		||||
 | 
			
		||||
# QuickStart
 | 
			
		||||
 | 
			
		||||
To get started, press I (shift-i) and add something to the document. Use the arrow keys to get around. To cancel a prompt, close a menu or close a dialog, press ctrl-C. When you are happy with your work, press ctrl-W and enter a filename to write. You can also press ctrl-R and read in a new file, overwriting your masterpiece.
 | 
			
		||||
 | 
			
		||||
# Useful keys
 | 
			
		||||
 | 
			
		||||
### Main Window
 | 
			
		||||
 | 
			
		||||
   ↑↓  Navigate values in document
 | 
			
		||||
    h  Show this help
 | 
			
		||||
    H  Show information about the app and license
 | 
			
		||||
    i  Insert a new value
 | 
			
		||||
    I  Insert value, array or object
 | 
			
		||||
    e  Edit selected value
 | 
			
		||||
    E  Edit selected key
 | 
			
		||||
Enter  Edit string/number, toggle booleans
 | 
			
		||||
    D  Delete selected key
 | 
			
		||||
    c  Toggle compact list view
 | 
			
		||||
    q  Toggle quoted keys in list view
 | 
			
		||||
   ^W  Write to file
 | 
			
		||||
   ^R  Read from file
 | 
			
		||||
   ^O  Open file browser
 | 
			
		||||
   ^N  New document with empty object
 | 
			
		||||
   ^X  Exit
 | 
			
		||||
 | 
			
		||||
### Dialogs/Menus
 | 
			
		||||
 | 
			
		||||
   ↑↓  Select/Scroll
 | 
			
		||||
Enter  Select value
 | 
			
		||||
   ^C  Cancel
 | 
			
		||||
 | 
			
		||||
# Doing stuff
 | 
			
		||||
 | 
			
		||||
### Adding keys or values
 | 
			
		||||
To add a key or a value, navigate to a value in an array or object, or to a specific array or object, and press "i". You will be prompted for the value, and for objects the key.
 | 
			
		||||
You can also press "I" to add arrays and objects. Just select what you want to add in the menu and press enter.
 | 
			
		||||
 | 
			
		||||
### Editing keys
 | 
			
		||||
You can edit keys on objects. For this, press "E".
 | 
			
		||||
 | 
			
		||||
### Editing values
 | 
			
		||||
To edit a value, press "e". The value is verbatim JSON, so strings should be quoted and all that. Anything that is unparsable JSON will be used as is, resulting in a string.
 | 
			
		||||
 | 
			
		||||
### Loading and Saving files
 | 
			
		||||
To load a file, press ^R and enter the filename to read. To write to a file, press ^W and enter the filename to write to.
 | 
			
		||||
 | 
			
		||||
### YAML or JSON?
 | 
			
		||||
There is no need to select YAML or JSON mode. All operations work the same, and the format is determined on load or save.
 | 
			
		||||
 | 
			
		||||
# Disclaimer
 | 
			
		||||
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:
 | 
			
		||||
 | 
			
		||||
# Support
 | 
			
		||||
Go to https://dev.noccylabs.info/noccy/jsonedit to find the source code, issue tracker, and learn more about the project!
 | 
			
		||||
EOT;
 | 
			
		||||
 
 | 
			
		||||
@@ -241,7 +241,7 @@ class TreeList implements Countable, IteratorAggregate
 | 
			
		||||
                'string' => "\e[33m",
 | 
			
		||||
                'integer' => "\e[94m",
 | 
			
		||||
                'double' => "\e[96m",
 | 
			
		||||
                'boolean' => "\e[35m",
 | 
			
		||||
                'boolean' => ($value?"\e[92m":"\e[32m"),
 | 
			
		||||
                'NULL' => "\e[31m",
 | 
			
		||||
                default => "",
 | 
			
		||||
            };
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user