From 062465ac5271f21211c5e09bb4e64b943c909424 Mon Sep 17 00:00:00 2001 From: Dominique Fournier Date: Mon, 11 Jun 2018 19:53:12 +0000 Subject: [PATCH] Console : manage the history and the autocompletion git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@4233 bf3deb0d-5f1a-0410-827f-c0cc1f45334c --- console.php | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 175 insertions(+), 6 deletions(-) diff --git a/console.php b/console.php index 2e7f7d8..d93be59 100644 --- a/console.php +++ b/console.php @@ -7,6 +7,7 @@ /** Allow to manage a linux Console to have a minimal but working text interface * When using this class, you must use the $console::echo method and not * display directely on screen + * Like readline, but all in PHP */ class console { @@ -30,6 +31,21 @@ class console private $nonWriteableChar = array (1, 2, 4, 6, 8, 9, 16, 18, 20, 21, 22, 23, 24, 25, 127); + /* The history list in an array + */ + private $history = array (); + + /** The history max size in entries + */ + private $historyMaxSize = 1000; + + /** Set the completion keys. Not set by default + */ + private $completionKeys = false; + + /** Set the function called when the completion char is called + */ + private $completionFunction = array (); // }}} /** The constructor init the console. @@ -140,14 +156,11 @@ class console // }}} /** Get the line of characters pressed by the user and return the result. - * Stop when the user valid by \n or one of the stoppers chars. If the - * stoppers chars are set, the last char of the returned string is the - * stopper char. If not set, the method do not return \n at the end. + * Stop when the user valid by \n. * Manage correctely the backspace, the Ctrl+W to remove word... - * @param string $stopperChar The stopper chars. * @return The typed string */ - public function gets ($stopperChar = false) + public function readline ($stopperChar = false) // {{{ { // Gets can not delete chars before the call. Keep the prompt (if exists) @@ -169,9 +182,43 @@ class console { // End of process with stopperChars $this->lineContent = ""; - $string .= $char; break; } + if ($this->completionKeys !== false && + in_array ($char, str_split ($this->completionKeys))) + { + // Manage autocompletion + $completeArr = call_user_func ($this->completionFunction, $string); + if (count ($completeArr) === 1) + { + if (substr ($string, -1) === " ") + { + // Next word is unique : add this to the string + $string .= reset ($completeArr); + } + else + { + // Continuous word is unique : replace the last word by the proposal + $pos = strrpos ($string, " "); + if ($pos === false) + $string = reset ($completeArr); + else + { + $string = substr ($string, 0, $pos +1); + $string .= reset ($completeArr); + } + } + } + else + { + // Multiple answers : display them + echo "\n".implode ("\n", $completeArr)."\n"; + } + echo "\r".str_repeat (" ", strlen ($this->lineContent))."\r"; + $this->lineContent = $prompt.$string; + echo $this->lineContent; + $cursorPos = mb_strlen ($this->lineContent); + } elseif (ord($char) === 21) { // Empty line next to prompt (Ctrl+U) @@ -327,6 +374,128 @@ class console } // }}} + /** Call a specific function when a completion key is pressed + * @param string|bool $completionKeys The list of the completion keys. False + * unset the method + * @param callable $completionFunction The function called when one of the + * completion keys is pressed. + * The function must get the partial text as first parameter, and must return + * an array with the possibilities + */ + public function completeFunction ($completionKeys, $completionFunction) + // {{{ + { + if (! is_string ($completionKeys) && ! is_boolean ($completionKeys)) + $this->consoleException ("Can not set the completionKeys : not a string"); + if ($completionKeys === true) + $this->consoleException ("Can not set the completionKeys : not false"); + if (! is_callable ($completionFunction)) + $this->consoleException ("Can not set the completionFunction : ". + "not a callable function"); + $this->completionKeys = $completionKeys; + $this->completionFunction = $completionFunction; + } + // }}} + + /** Clear the history + * Do NOT write the empty history on disk + */ + public function clearHistory () + // {{{ + { + $this->history = array (); + } + // }}} + + /** Write the history to disk. + * @param string $historyFile The history file where the history is stored + */ + public function writeHistory ($historyFile) + // {{{ + { + if (file_exists ($historyFile)) + { + if (! is_writeable ($historyFile)) + $this->consoleException ("History file '$historyFile' ". + "is not writeable"); + $history = file_get_contents ($historyFile); + if ($history === false) + $this->consoleException ("History file '$historyFile' can not be read"); + $historyArr = explode ("\n", $history); + if (! isset ($historyArr[0]) || $historyArr[0] !== "__HISTORY__") + $this->consoleException ("History file '$historyFile' ". + "is not an history file : do not touch\n"); + } + elseif (! file_exists (dirname ($historyFile))) + $this->consoleException ("History file '$historyFile' ". + "can not be created: parent directory doesn't exists"); + elseif (! is_dir (dirname ($historyFile))) + $this->consoleException ("History file '$historyFile' ". + "can not be created: parent directory is not a directory"); + file_put_contents ($historyFile, "__HISTORY__\n"); + return !! file_put_contents ($historyFile, implode ("\n", $this->history), + FILE_APPEND); + } + // }}} + + /** Read the history from the disk + * If the file doesn't exists, return an empty array + * @param string $historyFile The history file where the history is stored + */ + public function readHistory ($historyFile) + // {{{ + { + if (! file_exists ($historyFile)) + { + $this->history = array (); + return array (); + } + if (! is_readable ($historyFile)) + $this->consoleException ("History file '$historyFile' can not be read"); + $history = file_get_contents ($historyFile); + if ($history === false) + $this->consoleException ("History file '$historyFile' can not be read"); + $historyArr = explode ("\n", $history); + if (! isset ($historyArr[0]) || $historyArr[0] !== "__HISTORY__") + $this->consoleException ("History file '$historyFile' ". + "is not an history file : do not touch\n"); + array_shift ($historyArr); + $this->history = $historyArr; + return $this->history; + } + // }}} + + /** Add a new entry in history. + * Do NOT write the history on disk. + * @param string The new entry to add in history + */ + public function addHistory ($line) + // {{{ + { + if (! is_string ($line)) + $this->consoleException ("Can not add line to history : ". + "it is not a string"); + $this->history[] = $line; + $this->history = array_slice ($this->history, -$this->historyMaxSize); + } + // }}} + + /** Get/Set the maximum number of entries in the history + * If null, get the defined maximum number + * @param integer|null $historyMaxSize The maximum number of entries + */ + public function historyMaxSize ($historyMaxSize = null) + // {{{ + { + if ($historyMaxSize === null) + return $this->historyMaxSize; + if (intval ($historyMaxSize) < 1) + $this->consoleException ("Can not set the historyMaxSize : ". + "negative value provided"); + $this->historyMaxSize = intval ($historyMaxSize); + } + // }}} + /** Error management * @param string $message The message to throw in the exception */