You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

274 lines
5.5 KiB

<?php
/**
* SCSSPHP
*
* @copyright 2012-2018 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp;
use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\SourceMap\SourceMapGenerator;
/**
* Base formatter
*
* @author Leaf Corcoran <leafot@gmail.com>
*/
abstract class Formatter
{
/**
* @var integer
*/
public $indentLevel;
/**
* @var string
*/
public $indentChar;
/**
* @var string
*/
public $break;
/**
* @var string
*/
public $open;
/**
* @var string
*/
public $close;
/**
* @var string
*/
public $tagSeparator;
/**
* @var string
*/
public $assignSeparator;
/**
* @var boolean
*/
public $keepSemicolons;
/**
* @var \Leafo\ScssPhp\Formatter\OutputBlock
*/
protected $currentBlock;
/**
* @var integer
*/
protected $currentLine;
/**
* @var integer
*/
protected $currentColumn;
/**
* @var \Leafo\ScssPhp\SourceMap\SourceMapGenerator
*/
protected $sourceMapGenerator;
/**
* Initialize formatter
*
* @api
*/
abstract public function __construct();
/**
* Return indentation (whitespace)
*
* @return string
*/
protected function indentStr()
{
return '';
}
/**
* Return property assignment
*
* @api
*
* @param string $name
* @param mixed $value
*
* @return string
*/
public function property($name, $value)
{
return rtrim($name) . $this->assignSeparator . $value . ';';
}
/**
* Strip semi-colon appended by property(); it's a separator, not a terminator
*
* @api
*
* @param array $lines
*/
public function stripSemicolon(&$lines)
{
if ($this->keepSemicolons) {
return;
}
if (($count = count($lines))
&& substr($lines[$count - 1], -1) === ';'
) {
$lines[$count - 1] = substr($lines[$count - 1], 0, -1);
}
}
/**
* Output lines inside a block
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
*/
protected function blockLines(OutputBlock $block)
{
$inner = $this->indentStr();
$glue = $this->break . $inner;
$this->write($inner . implode($glue, $block->lines));
if (! empty($block->children)) {
$this->write($this->break);
}
}
/**
* Output block selectors
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
*/
protected function blockSelectors(OutputBlock $block)
{
$inner = $this->indentStr();
$this->write($inner
. implode($this->tagSeparator, $block->selectors)
. $this->open . $this->break);
}
/**
* Output block children
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
*/
protected function blockChildren(OutputBlock $block)
{
foreach ($block->children as $child) {
$this->block($child);
}
}
/**
* Output non-empty block
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block
*/
protected function block(OutputBlock $block)
{
if (empty($block->lines) && empty($block->children)) {
return;
}
$this->currentBlock = $block;
$pre = $this->indentStr();
if (! empty($block->selectors)) {
$this->blockSelectors($block);
$this->indentLevel++;
}
if (! empty($block->lines)) {
$this->blockLines($block);
}
if (! empty($block->children)) {
$this->blockChildren($block);
}
if (! empty($block->selectors)) {
$this->indentLevel--;
if (empty($block->children)) {
$this->write($this->break);
}
$this->write($pre . $this->close . $this->break);
}
}
/**
* Entry point to formatting a block
*
* @api
*
* @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree
* @param \Leafo\ScssPhp\SourceMap\SourceMapGenerator|null $sourceMapGenerator Optional source map generator
*
* @return string
*/
public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null)
{
$this->sourceMapGenerator = null;
if ($sourceMapGenerator) {
$this->currentLine = 1;
$this->currentColumn = 0;
$this->sourceMapGenerator = $sourceMapGenerator;
}
ob_start();
$this->block($block);
$out = ob_get_clean();
return $out;
}
/**
* @param string $str
*/
protected function write($str)
{
if ($this->sourceMapGenerator) {
$this->sourceMapGenerator->addMapping(
$this->currentLine,
$this->currentColumn,
$this->currentBlock->sourceLine,
$this->currentBlock->sourceColumn - 1, //columns from parser are off by one
$this->currentBlock->sourceName
);
$lines = explode("\n", $str);
$lineCount = count($lines);
$this->currentLine += $lineCount-1;
$lastLine = array_pop($lines);
$this->currentColumn = ($lineCount === 1 ? $this->currentColumn : 0) + strlen($lastLine);
}
echo $str;
}
}