5 * @copyright 2012-2018 Leaf Corcoran
7 * @license http://opensource.org/licenses/MIT MIT
9 * @link http://leafo.github.io/scssphp
12 namespace Leafo\ScssPhp
;
14 use Leafo\ScssPhp\Formatter\OutputBlock
;
15 use Leafo\ScssPhp\SourceMap\SourceMapGenerator
;
20 * @author Leaf Corcoran <leafot@gmail.com>
22 abstract class Formatter
57 public $assignSeparator;
62 public $keepSemicolons;
65 * @var \Leafo\ScssPhp\Formatter\OutputBlock
67 protected $currentBlock;
72 protected $currentLine;
77 protected $currentColumn;
80 * @var \Leafo\ScssPhp\SourceMap\SourceMapGenerator
82 protected $sourceMapGenerator;
85 * Initialize formatter
89 abstract public function __construct();
92 * Return indentation (whitespace)
96 protected function indentStr()
102 * Return property assignment
106 * @param string $name
107 * @param mixed $value
111 public function property($name, $value)
113 return rtrim($name) . $this->assignSeparator
. $value . ';';
117 * Strip semi-colon appended by property(); it's a separator, not a terminator
121 * @param array $lines
123 public function stripSemicolon(&$lines)
125 if ($this->keepSemicolons
) {
129 if (($count = count($lines))
130 && substr($lines[$count - 1], -1) === ';'
132 $lines[$count - 1] = substr($lines[$count - 1], 0, -1);
137 * Output lines inside a block
139 * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
141 protected function blockLines(OutputBlock
$block)
143 $inner = $this->indentStr();
145 $glue = $this->break . $inner;
147 $this->write($inner . implode($glue, $block->lines
));
149 if (! empty($block->children
)) {
150 $this->write($this->break);
155 * Output block selectors
157 * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
159 protected function blockSelectors(OutputBlock
$block)
161 $inner = $this->indentStr();
164 . implode($this->tagSeparator
, $block->selectors
)
165 . $this->open
. $this->break);
169 * Output block children
171 * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
173 protected function blockChildren(OutputBlock
$block)
175 foreach ($block->children
as $child) {
176 $this->block($child);
181 * Output non-empty block
183 * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
185 protected function block(OutputBlock
$block)
187 if (empty($block->lines
) && empty($block->children
)) {
191 $this->currentBlock
= $block;
193 $pre = $this->indentStr();
195 if (! empty($block->selectors
)) {
196 $this->blockSelectors($block);
198 $this->indentLevel++
;
201 if (! empty($block->lines
)) {
202 $this->blockLines($block);
205 if (! empty($block->children
)) {
206 $this->blockChildren($block);
209 if (! empty($block->selectors
)) {
210 $this->indentLevel
--;
212 if (empty($block->children
)) {
213 $this->write($this->break);
216 $this->write($pre . $this->close
. $this->break);
221 * Entry point to formatting a block
225 * @param \Leafo\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree
226 * @param \Leafo\ScssPhp\SourceMap\SourceMapGenerator|null $sourceMapGenerator Optional source map generator
230 public function format(OutputBlock
$block, SourceMapGenerator
$sourceMapGenerator = null)
232 $this->sourceMapGenerator
= null;
234 if ($sourceMapGenerator) {
235 $this->currentLine
= 1;
236 $this->currentColumn
= 0;
237 $this->sourceMapGenerator
= $sourceMapGenerator;
242 $this->block($block);
244 $out = ob_get_clean();
252 protected function write($str)
254 if ($this->sourceMapGenerator
) {
255 $this->sourceMapGenerator
->addMapping(
257 $this->currentColumn
,
258 $this->currentBlock
->sourceLine
,
259 $this->currentBlock
->sourceColumn
- 1, //columns from parser are off by one
260 $this->currentBlock
->sourceName
263 $lines = explode("\n", $str);
264 $lineCount = count($lines);
265 $this->currentLine +
= $lineCount-1;
267 $lastLine = array_pop($lines);
269 $this->currentColumn
= ($lineCount === 1 ?
$this->currentColumn
: 0) +
strlen($lastLine);