Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
/**
4
 * SCSSPHP
5
 *
6
 * @copyright 2012-2020 Leaf Corcoran
7
 *
8
 * @license http://opensource.org/licenses/MIT MIT
9
 *
10
 * @link http://scssphp.github.io/scssphp
11
 */
12
 
13
namespace ScssPhp\ScssPhp;
14
 
15
use ScssPhp\ScssPhp\Formatter\OutputBlock;
16
use ScssPhp\ScssPhp\SourceMap\SourceMapGenerator;
17
 
18
/**
19
 * Base formatter
20
 *
21
 * @author Leaf Corcoran <leafot@gmail.com>
22
 *
23
 * @internal
24
 */
25
abstract class Formatter
26
{
27
    /**
28
     * @var int
29
     */
30
    public $indentLevel;
31
 
32
    /**
33
     * @var string
34
     */
35
    public $indentChar;
36
 
37
    /**
38
     * @var string
39
     */
40
    public $break;
41
 
42
    /**
43
     * @var string
44
     */
45
    public $open;
46
 
47
    /**
48
     * @var string
49
     */
50
    public $close;
51
 
52
    /**
53
     * @var string
54
     */
55
    public $tagSeparator;
56
 
57
    /**
58
     * @var string
59
     */
60
    public $assignSeparator;
61
 
62
    /**
63
     * @var bool
64
     */
65
    public $keepSemicolons;
66
 
67
    /**
68
     * @var \ScssPhp\ScssPhp\Formatter\OutputBlock
69
     */
70
    protected $currentBlock;
71
 
72
    /**
73
     * @var int
74
     */
75
    protected $currentLine;
76
 
77
    /**
78
     * @var int
79
     */
80
    protected $currentColumn;
81
 
82
    /**
83
     * @var \ScssPhp\ScssPhp\SourceMap\SourceMapGenerator|null
84
     */
85
    protected $sourceMapGenerator;
86
 
87
    /**
88
     * @var string
89
     */
90
    protected $strippedSemicolon;
91
 
92
    /**
93
     * Initialize formatter
94
     *
95
     * @api
96
     */
97
    abstract public function __construct();
98
 
99
    /**
100
     * Return indentation (whitespace)
101
     *
102
     * @return string
103
     */
104
    protected function indentStr()
105
    {
106
        return '';
107
    }
108
 
109
    /**
110
     * Return property assignment
111
     *
112
     * @api
113
     *
114
     * @param string $name
115
     * @param mixed  $value
116
     *
117
     * @return string
118
     */
119
    public function property($name, $value)
120
    {
121
        return rtrim($name) . $this->assignSeparator . $value . ';';
122
    }
123
 
124
    /**
125
     * Return custom property assignment
126
     * differs in that you have to keep spaces in the value as is
127
     *
128
     * @api
129
     *
130
     * @param string $name
131
     * @param mixed  $value
132
     *
133
     * @return string
134
     */
135
    public function customProperty($name, $value)
136
    {
137
        return rtrim($name) . trim($this->assignSeparator) . $value . ';';
138
    }
139
 
140
    /**
141
     * Output lines inside a block
142
     *
143
     * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
144
     *
145
     * @return void
146
     */
147
    protected function blockLines(OutputBlock $block)
148
    {
149
        $inner = $this->indentStr();
150
        $glue  = $this->break . $inner;
151
 
152
        $this->write($inner . implode($glue, $block->lines));
153
 
154
        if (! empty($block->children)) {
155
            $this->write($this->break);
156
        }
157
    }
158
 
159
    /**
160
     * Output block selectors
161
     *
162
     * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
163
     *
164
     * @return void
165
     */
166
    protected function blockSelectors(OutputBlock $block)
167
    {
168
        assert(! empty($block->selectors));
169
 
170
        $inner = $this->indentStr();
171
 
172
        $this->write($inner
173
            . implode($this->tagSeparator, $block->selectors)
174
            . $this->open . $this->break);
175
    }
176
 
177
    /**
178
     * Output block children
179
     *
180
     * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
181
     *
182
     * @return void
183
     */
184
    protected function blockChildren(OutputBlock $block)
185
    {
186
        foreach ($block->children as $child) {
187
            $this->block($child);
188
        }
189
    }
190
 
191
    /**
192
     * Output non-empty block
193
     *
194
     * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
195
     *
196
     * @return void
197
     */
198
    protected function block(OutputBlock $block)
199
    {
200
        if (empty($block->lines) && empty($block->children)) {
201
            return;
202
        }
203
 
204
        $this->currentBlock = $block;
205
 
206
        $pre = $this->indentStr();
207
 
208
        if (! empty($block->selectors)) {
209
            $this->blockSelectors($block);
210
 
211
            $this->indentLevel++;
212
        }
213
 
214
        if (! empty($block->lines)) {
215
            $this->blockLines($block);
216
        }
217
 
218
        if (! empty($block->children)) {
219
            $this->blockChildren($block);
220
        }
221
 
222
        if (! empty($block->selectors)) {
223
            $this->indentLevel--;
224
 
225
            if (! $this->keepSemicolons) {
226
                $this->strippedSemicolon = '';
227
            }
228
 
229
            if (empty($block->children)) {
230
                $this->write($this->break);
231
            }
232
 
233
            $this->write($pre . $this->close . $this->break);
234
        }
235
    }
236
 
237
    /**
238
     * Test and clean safely empty children
239
     *
240
     * @param \ScssPhp\ScssPhp\Formatter\OutputBlock $block
241
     *
242
     * @return bool
243
     */
244
    protected function testEmptyChildren($block)
245
    {
246
        $isEmpty = empty($block->lines);
247
 
248
        if ($block->children) {
249
            foreach ($block->children as $k => &$child) {
250
                if (! $this->testEmptyChildren($child)) {
251
                    $isEmpty = false;
252
                    continue;
253
                }
254
 
255
                if ($child->type === Type::T_MEDIA || $child->type === Type::T_DIRECTIVE) {
256
                    $child->children = [];
257
                    $child->selectors = null;
258
                }
259
            }
260
        }
261
 
262
        return $isEmpty;
263
    }
264
 
265
    /**
266
     * Entry point to formatting a block
267
     *
268
     * @api
269
     *
270
     * @param \ScssPhp\ScssPhp\Formatter\OutputBlock             $block              An abstract syntax tree
271
     * @param \ScssPhp\ScssPhp\SourceMap\SourceMapGenerator|null $sourceMapGenerator Optional source map generator
272
     *
273
     * @return string
274
     */
275
    public function format(OutputBlock $block, SourceMapGenerator $sourceMapGenerator = null)
276
    {
277
        $this->sourceMapGenerator = null;
278
 
279
        if ($sourceMapGenerator) {
280
            $this->currentLine        = 1;
281
            $this->currentColumn      = 0;
282
            $this->sourceMapGenerator = $sourceMapGenerator;
283
        }
284
 
285
        $this->testEmptyChildren($block);
286
 
287
        ob_start();
288
 
289
        try {
290
            $this->block($block);
291
        } catch (\Exception $e) {
292
            ob_end_clean();
293
            throw $e;
294
        } catch (\Throwable $e) {
295
            ob_end_clean();
296
            throw $e;
297
        }
298
 
299
        $out = ob_get_clean();
300
        assert($out !== false);
301
 
302
        return $out;
303
    }
304
 
305
    /**
306
     * Output content
307
     *
308
     * @param string $str
309
     *
310
     * @return void
311
     */
312
    protected function write($str)
313
    {
314
        if (! empty($this->strippedSemicolon)) {
315
            echo $this->strippedSemicolon;
316
 
317
            $this->strippedSemicolon = '';
318
        }
319
 
320
        /*
321
         * Maybe Strip semi-colon appended by property(); it's a separator, not a terminator
322
         * will be striped for real before a closing, otherwise displayed unchanged starting the next write
323
         */
324
        if (
325
            ! $this->keepSemicolons &&
326
            $str &&
327
            (strpos($str, ';') !== false) &&
328
            (substr($str, -1) === ';')
329
        ) {
330
            $str = substr($str, 0, -1);
331
 
332
            $this->strippedSemicolon = ';';
333
        }
334
 
335
        if ($this->sourceMapGenerator) {
336
            $lines = explode("\n", $str);
337
            $lastLine = array_pop($lines);
338
 
339
            foreach ($lines as $line) {
340
                // If the written line starts is empty, adding a mapping would add it for
341
                // a non-existent column as we are at the end of the line
342
                if ($line !== '') {
343
                    assert($this->currentBlock->sourceLine !== null);
344
                    assert($this->currentBlock->sourceName !== null);
345
                    $this->sourceMapGenerator->addMapping(
346
                        $this->currentLine,
347
                        $this->currentColumn,
348
                        $this->currentBlock->sourceLine,
349
                        //columns from parser are off by one
350
                        $this->currentBlock->sourceColumn > 0 ? $this->currentBlock->sourceColumn - 1 : 0,
351
                        $this->currentBlock->sourceName
352
                    );
353
                }
354
 
355
                $this->currentLine++;
356
                $this->currentColumn = 0;
357
            }
358
 
359
            if ($lastLine !== '') {
360
                assert($this->currentBlock->sourceLine !== null);
361
                assert($this->currentBlock->sourceName !== null);
362
                $this->sourceMapGenerator->addMapping(
363
                    $this->currentLine,
364
                    $this->currentColumn,
365
                    $this->currentBlock->sourceLine,
366
                    //columns from parser are off by one
367
                    $this->currentBlock->sourceColumn > 0 ? $this->currentBlock->sourceColumn - 1 : 0,
368
                    $this->currentBlock->sourceName
369
                );
370
            }
371
 
372
            $this->currentColumn += \strlen($lastLine);
373
        }
374
 
375
        echo $str;
376
    }
377
}