MDL-71669 editor_atto: Fire custom event when toggling button highlight
[moodle.git] / lib / scssphp / Formatter.php
blobe17770458a99d44cddd10f81a346ede60fa158e9
1 <?php
2 /**
3 * SCSSPHP
5 * @copyright 2012-2019 Leaf Corcoran
7 * @license http://opensource.org/licenses/MIT MIT
9 * @link http://scssphp.github.io/scssphp
12 namespace ScssPhp\ScssPhp;
14 use ScssPhp\ScssPhp\Formatter\OutputBlock;
15 use ScssPhp\ScssPhp\SourceMap\SourceMapGenerator;
17 /**
18 * Base formatter
20 * @author Leaf Corcoran <leafot@gmail.com>
22 abstract class Formatter
24 /**
25 * @var integer
27 public $indentLevel;
29 /**
30 * @var string
32 public $indentChar;
34 /**
35 * @var string
37 public $break;
39 /**
40 * @var string
42 public $open;
44 /**
45 * @var string
47 public $close;
49 /**
50 * @var string
52 public $tagSeparator;
54 /**
55 * @var string
57 public $assignSeparator;
59 /**
60 * @var boolean
62 public $keepSemicolons;
64 /**
65 * @var \ScssPhp\ScssPhp\Formatter\OutputBlock
67 protected $currentBlock;
69 /**
70 * @var integer
72 protected $currentLine;
74 /**
75 * @var integer
77 protected $currentColumn;
79 /**
80 * @var \ScssPhp\ScssPhp\SourceMap\SourceMapGenerator
82 protected $sourceMapGenerator;
84 /**
85 * @var string
87 protected $strippedSemicolon;
89 /**
90 * Initialize formatter
92 * @api
94 abstract public function __construct();
96 /**
97 * Return indentation (whitespace)
99 * @return string
101 protected function indentStr()
103 return '';
107 * Return property assignment
109 * @api
111 * @param string $name
112 * @param mixed $value
114 * @return string
116 public function property($name, $value)
118 return rtrim($name) . $this->assignSeparator . $value . ';';
122 * Output lines inside a block
124 * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
126 protected function blockLines(OutputBlock $block)
128 $inner = $this->indentStr();
130 $glue = $this->break . $inner;
132 $this->write($inner . implode($glue, $block->lines));
134 if (! empty($block->children)) {
135 $this->write($this->break);
140 * Output block selectors
142 * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
144 protected function blockSelectors(OutputBlock $block)
146 $inner = $this->indentStr();
148 $this->write($inner
149 . implode($this->tagSeparator, $block->selectors)
150 . $this->open . $this->break);
154 * Output block children
156 * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
158 protected function blockChildren(OutputBlock $block)
160 foreach ($block->children as $child) {
161 $this->block($child);
166 * Output non-empty block
168 * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
170 protected function block(OutputBlock $block)
172 if (empty($block->lines) && empty($block->children)) {
173 return;
176 $this->currentBlock = $block;
178 $pre = $this->indentStr();
180 if (! empty($block->selectors)) {
181 $this->blockSelectors($block);
183 $this->indentLevel++;
186 if (! empty($block->lines)) {
187 $this->blockLines($block);
190 if (! empty($block->children)) {
191 $this->blockChildren($block);
194 if (! empty($block->selectors)) {
195 $this->indentLevel--;
197 if (! $this->keepSemicolons) {
198 $this->strippedSemicolon = '';
201 if (empty($block->children)) {
202 $this->write($this->break);
205 $this->write($pre . $this->close . $this->break);
210 * Test and clean safely empty children
212 * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
214 * @return boolean
216 protected function testEmptyChildren($block)
218 $isEmpty = empty($block->lines);
220 if ($block->children) {
221 foreach ($block->children as $k => &$child) {
222 if (! $this->testEmptyChildren($child)) {
223 $isEmpty = false;
224 continue;
227 if ($child->type === Type::T_MEDIA || $child->type === Type::T_DIRECTIVE) {
228 $child->children = [];
229 $child->selectors = null;
234 return $isEmpty;
238 * Entry point to formatting a block
240 * @api
242 * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block An abstract syntax tree
243 * @param \ScssPhp\ScssPhp\SourceMap\SourceMapGenerator|null $sourceMapGenerator Optional source map generator
245 * @return string
247 public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null)
249 $this->sourceMapGenerator = null;
251 if ($sourceMapGenerator) {
252 $this->currentLine = 1;
253 $this->currentColumn = 0;
254 $this->sourceMapGenerator = $sourceMapGenerator;
257 $this->testEmptyChildren($block);
259 ob_start();
261 $this->block($block);
263 $out = ob_get_clean();
265 return $out;
269 * Output content
271 * @param string $str
273 protected function write($str)
275 if (! empty($this->strippedSemicolon)) {
276 echo $this->strippedSemicolon;
278 $this->strippedSemicolon = '';
282 * Maybe Strip semi-colon appended by property(); it's a separator, not a terminator
283 * will be striped for real before a closing, otherwise displayed unchanged starting the next write
285 if (! $this->keepSemicolons &&
286 $str &&
287 (strpos($str, ';') !== false) &&
288 (substr($str, -1) === ';')
290 $str = substr($str, 0, -1);
292 $this->strippedSemicolon = ';';
295 if ($this->sourceMapGenerator) {
296 $this->sourceMapGenerator->addMapping(
297 $this->currentLine,
298 $this->currentColumn,
299 $this->currentBlock->sourceLine,
300 //columns from parser are off by one
301 $this->currentBlock->sourceColumn > 0 ? $this->currentBlock->sourceColumn - 1 : 0,
302 $this->currentBlock->sourceName
305 $lines = explode("\n", $str);
306 $lineCount = count($lines);
307 $this->currentLine += $lineCount-1;
309 $lastLine = array_pop($lines);
311 $this->currentColumn = ($lineCount === 1 ? $this->currentColumn : 0) + strlen($lastLine);
314 echo $str;