From 76f234d69ccb5d5d509ddcf619a7a5e0112982ab Mon Sep 17 00:00:00 2001 From: Christopher Vagnetoft Date: Sat, 17 Dec 2016 14:34:56 +0100 Subject: [PATCH] Customizable icons, new experimental theme parser --- README.md | 7 +- bin/phpl-generate | 6 +- bin/phpl-reload | 16 ++++- docs/THEMES-HOWTO.md | 57 +++++++++++++--- lib/bootstrap.php | 16 +++++ lib/theme.php | 150 +++++++++++++++++++++++++++++++++++++++++++ modules/git.php | 18 ++++-- modules/loadavg.php | 3 +- modules/thermal.php | 3 +- modules/tux.php | 14 ++++ themes/classic.theme | 15 +++++ themes/default.theme | 2 + themes/gray.theme | 1 + 13 files changed, 285 insertions(+), 23 deletions(-) create mode 100644 lib/theme.php create mode 100644 modules/tux.php create mode 100644 themes/classic.theme diff --git a/README.md b/README.md index a09e8a0..6936d21 100644 --- a/README.md +++ b/README.md @@ -137,14 +137,13 @@ You can reload your theme by switching to it again using `phpl-config`, or by ca To use the prompt in other ansi-capable scenarios, you can call on `phpl-generate`. By default, escape sequences will be enclosed within a block of `\[ .. \]`, but this can be disabled by passing `-r`. You can also specify the working directory with `-d` -and exit code of last process with `-s`. +and exit code of last process with `-s`. To add a newline to the end of the output, +use `-n`. ## ToDo/Known Issues Things that need improving: - * The theme parser shouldn't be line-oriented nor care about whitespace. - * Icons should be customizable, maybe as icon packs. * Better handling of 256-color and 24b-color stuff. Pragmas are in place but not active. * Need to strip UTF-8 when outputting to a physical console as most icons fail there. - * Solarized theme need dark and light variants, and `$include()` must be added to theme parser. + * Solarized theme need dark and light variants. diff --git a/bin/phpl-generate b/bin/phpl-generate index 55386b0..a4c1627 100755 --- a/bin/phpl-generate +++ b/bin/phpl-generate @@ -12,10 +12,11 @@ function print_help() { printf(" -s,--status Status of last command\n"); printf(" -r,--raw Don't escape ansi for PS1\n"); printf(" -q Quiet, don't output anything\n"); + printf(" -n Print a newline after the prompt output\n"); printf("\n"); } -$opts = cmdl_parse("hd:s:rq",[ +$opts = cmdl_parse("hd:s:rqn",[ "h" => "help", "r" => "raw", "d" => "dir:", @@ -42,3 +43,6 @@ if (cmdl_get($opts,'q')) { } else { require_once PHPL_STATE; } +if (cmdl_get($opts,'n')) { + echo "\n"; +} \ No newline at end of file diff --git a/bin/phpl-reload b/bin/phpl-reload index d368b99..841fa38 100755 --- a/bin/phpl-reload +++ b/bin/phpl-reload @@ -3,6 +3,7 @@ require_once __DIR__."/../lib/bootstrap.php"; require_once __DIR__."/../lib/config.php"; +require_once __DIR__."/../lib/theme.php"; function print_help() { printf("phpl-reload: Activate changes to the configuration\n\n"); @@ -94,7 +95,7 @@ function reload_prompt() { } function reload_theme() { - global $_CONFIG; + global $_CONFIG,$_ICONS; $style = null; @@ -105,10 +106,13 @@ function reload_theme() { printf("Error: The theme %s could not be found at %s\n", $theme, $theme_file); exit(1); } + + /* $data = array_map("trim",file($theme_file,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)); $vars = []; $rules = []; $rule = null; + $icons = []; $pragma_256color = false; $pragma_truecolor = false; @@ -127,6 +131,8 @@ function reload_theme() { default: printf("Warning: Unrecognized pragma %s\n", $match[1]); } + } elseif (preg_match("/\\\$icon\((.+?),(.+?)\)/", $line, $match)) { + $icons[$match[1]] = $match[2]; } elseif (preg_match("/\\\$set\((.+?),(.+?)\)/", $line, $match)) { $vars[$match[1]] = $match[2]; } elseif (strpos($line,'{')!==false) { @@ -159,8 +165,16 @@ function reload_theme() { } else { $head = null; } + */ + + $themeLoader = new Theme(); + $themeLoader->readTheme($theme_file); + $head = null; + $rules = $themeLoader->getRulesets(); + $icons = $themeLoader->getIcons(); $style = '$_THEME = new Theme('.var_export($rules,true).');'; + $style.= 'PHPL::$THEMEICONS = '.var_export($icons,true).';'; file_put_contents(PHPL_CACHE_THEME, "|} - Padding before text + pad-before: 1; + +Padding after text: pad-after: {none||} - Padding after text + pad-after: 1; + +Foreground color: - color: - Foreground color + color: {none||} + color: #000000; + +Background color: - background: - Background color + background: {none||} + background: white; + +Text style (separate multiple with space): + style: {none,bold,italic,underline} + style: bold italic; ## Variables * To set: `$set(,)` * To reference: `%` + $set(mycolor,red); + .class { + color: %mycolor; + } + Example: set(pretty,#4488BB) color: %pretty -## Including +## Including other styles - * `$include()`: Include the specified file + * `$import()`: Include the file *if it can be found*. Will not report errors. + * `$include()`: Include the specified file, error out if it can not be found. + +You can use these two directives to create a theme that uses shared components and allow +the user to modify aspects of it by creating the appropriate file: + + // This is mytheme-white.theme + $include(mytheme.common) + $import(mytheme.custom) + * { background: white; } + +The file `mytheme.custom` will only be included if it is found alongside `mytheme-white.theme` +while the file `mytheme.common` will always be included. ## Pragmas +Don't use the pragmas. They will change a lot. + * `$pragma(256color)`: Used to enable 256-color mode - * `$pragma(16mcolor)`: Used to enable 16.7m color mode + * `$pragma(truecolor)`: Used to enable 16.7m color mode + +## Overriding icons + +You can override icons with the `$icon(name,icon)` directive. The following will +use the letter "Y" as the branch icon for the git module. Unicode is allowed. Do not quote +the icon string as it is not parsed by the tokenizer but in a preprocessing step. This +also means that you can not use the character `)` as an icon right now. + $icon(git.branch,Y) \ No newline at end of file diff --git a/lib/bootstrap.php b/lib/bootstrap.php index 6c98793..592cabd 100644 --- a/lib/bootstrap.php +++ b/lib/bootstrap.php @@ -54,3 +54,19 @@ function cmdl_get($parsed, $key) { } return $parsed[$key]; } + +class PHPL { + public static $CONFIG=[]; + public static $THEME=[]; + public static $ICONS=[]; + public static $THEMEICONS=[]; +} + +function icon($name) { + return array_key_exists($name,PHPL::$THEMEICONS)?PHPL::$THEMEICONS[$name]: + (array_key_exists($name,PHPL::$ICONS)?PHPL::$ICONS[$name]:null); +} + +function seticon($name, $icon) { + PHPL::$ICONS[$name] = $icon; +} \ No newline at end of file diff --git a/lib/theme.php b/lib/theme.php new file mode 100644 index 0000000..383c711 --- /dev/null +++ b/lib/theme.php @@ -0,0 +1,150 @@ +rulesets as $rule=>$ruleset) { + printf("%s ----\n%s\n\n", $rule, $ruleset->getPropExport()); + } + + } + + public function readTheme($file) { + + $theme = file_get_contents($file); + + // Strip out all the directives $pragma, $set, $icon etc before parsing + //echo $theme."\n\n"; + preg_match_all('/\$(pragma|icon|set|import|include)\((.+?)\)/', $theme, $directives); + $theme = preg_replace('/\$(pragma|icon|set|import|include)\((.+?)\)/', "", $theme); + + $d_vars = []; // vars from $set() directives + $d_icons = []; // icons from $icon() directives + $d_pragma = []; // pragmas from $pragma() directives + + $rulesets = []; + + // parse directives + $count = count($directives[0]); + for ($index = 0; $index < $count; $index++) { + $directive = $directives[1][$index]; + $value = $directives[2][$index]; + switch ($directive) { + case 'pragma': + $d_pragma[] = $value; + break; + case 'icon': + list($name,$value) = explode(",",$value,2); + $d_icons[$name] = $value; + break; + case 'import': + $path = dirname($file).DIRECTORY_SEPARATOR.$value; + if (file_exists($path)) + $this->readTheme($path); + break; + case 'include': + $path = dirname($file).DIRECTORY_SEPARATOR.$value; + if (!file_exists($path)) { + printf("Error: Could not find requested file to include %s\n", $path); + exit(1); + } + $this->readTheme($path); + break; + case 'set': + list($name,$value) = explode(",",$value,2); + $d_vars[$name] = $value; + break; + } + } + + // Use the PHP tokenizer for parsing; # is used for comments, so + // we substitute it with 'rgb/' first, i.e. '#fed' => 'rgb/fed' + $theme = str_replace('#','rgb/', $theme); + $toks = token_get_all("rulesets = array_merge($this->rulesets, $rulesets); + $this->icons = array_merge($this->icons, $d_icons); + } + + public function getRulesets() + { + return $this->rulesets; + } + + public function getIcons() + { + return $this->icons; + } + +} + diff --git a/modules/git.php b/modules/git.php index 7ae6bd7..dddfa4a 100644 --- a/modules/git.php +++ b/modules/git.php @@ -26,24 +26,30 @@ function _git(array $opts) { if ($opts[GIT_SHOW_STATUS]==true) { exec("{$git} status --porcelain --branch", $status); if (count($status)>1) - $marks[] = ICON_GIT_BRANCH_CHANGED_SYMBOL; + $marks[] = icon("git.is_changed"); else - $marks[] = ICON_GIT_BRANCH_CURRENT; + $marks[] = icon("git.is_current"); if (preg_match('/\[ahead ([0-9]+)\]/', $status[0], $match)) - $marks[] = ICON_GIT_NEED_PUSH_SYMBOL.$match[1]; + $marks[] = icon("git.need_push").$match[1]; if (preg_match('/\[behind ([0-9]+)\]/', $status[0], $match)) - $marks[] = ICON_GIT_NEED_PULL_SYMBOL.$match[1]; + $marks[] = icon("git.need_pull").$match[1]; } if ($opts[GIT_SHOW_TAG]) { $tag = exec("{$git} describe --tags 2>/dev/null"); if ($tag) - $marks[] = ICON_GIT_TAG.(strpos($tag,'-')?substr($tag,0,strpos($tag,'-')).'+':$tag); + $marks[] = icon("git.tag").(strpos($tag,'-')?substr($tag,0,strpos($tag,'-')).'+':$tag); } - return panel(sprintf("%s %s %s", ICON_GIT_BRANCH_SYMBOL, $branch, join(" ",$marks)), ['class'=>'vcs'], 'git'); + return panel(sprintf("%s%s %s", icon('git.branch'), $branch, join(" ",$marks)), ['class'=>'vcs'], 'git'); } module("git", "Git VCS status", [ "git", "vcs" ]); option("tag", GIT_SHOW_TAG, OPT_TYPE_BOOL, "Show tag", false); option("status", GIT_SHOW_STATUS, OPT_TYPE_BOOL, "Show branch and status", true); +seticon("git.branch"," "); // "β‘‚"); +seticon("git.is_changed"," "); // "); //πŸ—˜"); +seticon("git.is_current",""); +seticon("git.need_push","οƒ˜"); // ⇑"); +seticon("git.need_pull","οƒ—"); // "⇣"); +seticon("git.tag"," "); diff --git a/modules/loadavg.php b/modules/loadavg.php index bd9cbbf..8d17362 100644 --- a/modules/loadavg.php +++ b/modules/loadavg.php @@ -2,6 +2,7 @@ function _loadavg() { - return panel(sprintf("ο‡Ύ %s",sys_getloadavg()[0]),[],'loadavg'); + return panel(sprintf("%s%s",icon('loadavg.load'), sys_getloadavg()[0]),[],'loadavg'); } module("loadavg", "Display system load average", [ "info", "loadavg" ]); +seticon("loadavg.load","ο‡Ύ "); \ No newline at end of file diff --git a/modules/thermal.php b/modules/thermal.php index 1e061d4..68bb4d6 100644 --- a/modules/thermal.php +++ b/modules/thermal.php @@ -9,7 +9,7 @@ function _thermal(array $opts) { $zone = $opts[THERMAL_ZONE]; $temp = (int)@trim(file_get_contents("/sys/class/thermal/{$zone}/temp")); $temp = (float)$temp/1000; - $text = sprintf("🌑 ".$opts[THERMAL_FORMAT], $temp); + $text = sprintf("%s".$opts[THERMAL_FORMAT], icon('thermal.temp'), $temp); $attr['class'] = $opts[THERMAL_CLASS]; return panel($text, $attr, 'thermal'); } @@ -17,3 +17,4 @@ function _thermal(array $opts) { option("zone", THERMAL_ZONE, OPT_TYPE_STRING, "Thermal zone (from /sys/class/thermal)", "thermal_zone0"); option("format", THERMAL_FORMAT, OPT_TYPE_STRING, "Format", "%.1fΒΊc"); option("class", THERMAL_CLASS, OPT_TYPE_STRING, "The class to use", "system"); +seticon("thermal.temp", ""); \ No newline at end of file diff --git a/modules/tux.php b/modules/tux.php new file mode 100644 index 0000000..5d04956 --- /dev/null +++ b/modules/tux.php @@ -0,0 +1,14 @@ +) +$icon(git.is_changed,~) +$icon(git.is_current,) + +$icon(loadavg.load,) +$icon(thermal.temp,) diff --git a/themes/default.theme b/themes/default.theme index a97e1c4..e057fe6 100644 --- a/themes/default.theme +++ b/themes/default.theme @@ -1,3 +1,5 @@ +$import(user.icons); + // Defaults, applies to everything * { color: bright-white; diff --git a/themes/gray.theme b/themes/gray.theme index 0ad33c2..83c3ee3 100644 --- a/themes/gray.theme +++ b/themes/gray.theme @@ -23,3 +23,4 @@ path { .info { color: gray; } +