Skip to content

Commit

Permalink
Customizable icons, new experimental theme parser
Browse files Browse the repository at this point in the history
  • Loading branch information
noccy80 committed Dec 17, 2016
1 parent 96f66dc commit 76f234d
Show file tree
Hide file tree
Showing 13 changed files with 285 additions and 23 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
6 changes: 5 additions & 1 deletion bin/phpl-generate
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ function print_help() {
printf(" -s,--status <code> 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:",
Expand All @@ -42,3 +43,6 @@ if (cmdl_get($opts,'q')) {
} else {
require_once PHPL_STATE;
}
if (cmdl_get($opts,'n')) {
echo "\n";
}
16 changes: 15 additions & 1 deletion bin/phpl-reload
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -94,7 +95,7 @@ function reload_prompt() {
}

function reload_theme() {
global $_CONFIG;
global $_CONFIG,$_ICONS;

$style = null;

Expand All @@ -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;
Expand All @@ -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) {
Expand Down Expand Up @@ -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, "<?php {$head} {$style}");

Expand Down
57 changes: 48 additions & 9 deletions docs/THEMES-HOWTO.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,74 @@ Examples:

## Attributes

Padding before text:

pad-before: {none|<int>|<string>}
Padding before text
pad-before: 1;

Padding after text:

pad-after: {none|<int>|<string>}
Padding after text
pad-after: 1;

Foreground color:

color: <color>
Foreground color
color: {none|<colorname>|<hex>}
color: #000000;

Background color:

background: <color>
Background color
background: {none|<colorname>|<hex>}
background: white;

Text style (separate multiple with space):

style: {none,bold,italic,underline}
style: bold italic;

## Variables

* To set: `$set(<name>,<value>)`
* To reference: `%<name>`

$set(mycolor,red);
.class {
color: %mycolor;
}

Example:

set(pretty,#4488BB)
color: %pretty

## Including
## Including other styles

* `$include(<file>)`: Include the specified file
* `$import(<file>)`: Include the file *if it can be found*. Will not report errors.
* `$include(<file>)`: 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)
16 changes: 16 additions & 0 deletions lib/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
150 changes: 150 additions & 0 deletions lib/theme.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
<?php

class Theme {

private $rulesets = [];

private $icons = [];

public function compile() {

foreach ($this->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("<?php ".$theme);
array_shift($toks);

$p_buf = null; // buffer for currently parsed keyword
$p_ruleset = null; // name of ruleset
$p_property = null; // property name
$p_value = null; // property value

// Walk and process values
foreach ($toks as $tok) {
if (is_array($tok)) {
switch ($tok[0]) {
case T_COMMENT:
continue;
case T_WHITESPACE:
$p_buf.= " ";
break;
case T_STRING:
case T_VARIABLE:
case T_LNUMBER:
case T_DNUMBER:
$p_buf.=$tok[1];
break;
default:
$typ = token_name($tok[0]);
$str = $tok[1];
printf("%s (%s)\n", $tok[1], token_name($tok[0]));
}
} else {
switch ($tok) {
case '{':
$p_ruleset = trim($p_buf);
$p_buf = null;
empty($rulesets[$p_ruleset]) && ($rulesets[$p_ruleset] = []);
//printf("open propset, ruleset=%s\n", $p_ruleset);
break;
case '}':
//printf("end propset, ruleset=%s\n", $p_ruleset);
$p_ruleset = null;
$p_buf = null;
break;
case ':':
$p_property = trim($p_buf);
$p_buf = null;
//printf("begin property, ruleset=%s prop=%s\n", $p_ruleset, $p_property);
break;
case ';':
$value = trim($p_buf);
if ($value && ($value[0]=='%')) {
$var = substr($value,1);
if (!array_key_exists($var,$d_vars)) {
printf("Error: Referencing undefined variable %s\n", $value);
exit(1);
}
$value = $d_vars[$var];
}
$rulesets[$p_ruleset][$p_property] = $value;
//printf("property: ruleset=%s prop=%s value=%s raw=%s\n", $p_ruleset, $p_property, $value, $p_buf);
$p_property = null;
$p_buf = null;
break;
default:
$p_buf.=$tok;
}
}

}
$this->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;
}

}

18 changes: 12 additions & 6 deletions modules/git.php
Original file line number Diff line number Diff line change
Expand Up @@ -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","");
3 changes: 2 additions & 1 deletion modules/loadavg.php
Original file line number Diff line number Diff line change
Expand Up @@ -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","");
Loading

0 comments on commit 76f234d

Please sign in to comment.