DiffConsoleOutput::write()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 3
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of graze/console-diff-renderer.
5
 *
6
 * Copyright (c) 2017 Nature Delivered Ltd. <https://www.graze.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * @license https://github.com/graze/console-diff-renderer/blob/master/LICENSE.md
12
 * @link    https://github.com/graze/console-diff-renderer
13
 */
14
15
namespace Graze\DiffRenderer;
16
17
use Graze\DiffRenderer\Diff\ConsoleDiff;
18
use Graze\DiffRenderer\Terminal\Terminal;
19
use Graze\DiffRenderer\Terminal\TerminalInterface;
20
use Graze\DiffRenderer\Wrap\Wrapper;
21
use Symfony\Component\Console\Formatter\OutputFormatterInterface;
22
use Symfony\Component\Console\Output\OutputInterface;
23
24
/**
25
 * This takes an array of lines to write to the console, does a different and only over-writes what has changed to the
26
 * console
27
 */
28
class DiffConsoleOutput implements OutputInterface
29
{
30
    /** @var string[] */
31
    private $buffer = [];
32
    /** @var ConsoleDiff */
33
    private $diff;
34
    /** @var TerminalInterface */
35
    private $terminal;
36
    /** @var OutputInterface */
37
    private $output;
38
    /** @var Wrapper */
39
    private $wrapper;
40
    /** @var bool */
41
    private $trim = false;
42
43
    /**
44
     * Constructor.
45
     *
46
     * @param OutputInterface   $output
47
     * @param TerminalInterface $terminal
48
     * @param Wrapper           $wrapper
49
     */
50 28
    public function __construct(
51
        OutputInterface $output,
52
        TerminalInterface $terminal = null,
53
        Wrapper $wrapper = null
54
    ) {
55 28
        $this->output = $output;
56 28
        $this->terminal = $terminal ?: new Terminal();
57 28
        $this->diff = new ConsoleDiff($terminal);
58 28
        $this->wrapper = $wrapper ?: new Wrapper($this->terminal);
59 28
    }
60
61
    /**
62
     * @param string|array $messages The message as an array of lines or a single string
63
     * @param bool         $newline  Whether to add a newline
64
     * @param int          $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered
65
     *                               the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
66
     */
67 2
    public function write($messages, $newline = false, $options = 0)
68
    {
69 2
        $this->buffer = [];
70 2
        $this->output->write($messages, $newline, $options);
71 2
    }
72
73
    /**
74
     * @param string|string[] $messages The message as an array of lines or a single string
75
     * @param bool            $newline  Whether to add a newline
76
     * @param int             $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered
77
     *                                  the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
78
     */
79 13
    public function reWrite($messages, $newline = false, $options = 0)
80
    {
81 13
        $messages = $this->format($messages, $options);
82 13
        if (count($messages) === 0) {
83 1
            return;
84
        }
85
86 12
        $messages = $this->splitNewLines($messages);
87 12
        $messages = ($this->trim) ? $this->wrapper->trim($messages) : $this->wrapper->wrap($messages);
88
89 12
        $outputOptions = self::OUTPUT_RAW | $this->output->getVerbosity();
90
91 12
        if (count($this->buffer) === 0) {
92 12
            $this->buffer = $messages;
93 12
            if ($newline) {
94 2
                $this->output->write($messages, true, $outputOptions);
95 2
            } else {
96 10
                $i = 0;
97 10
                $total = count($messages);
98 10
                foreach ($messages as $message) {
99 10
                    $this->output->write($message, ++$i < $total, $outputOptions);
100 10
                }
101
            }
102 12
            return;
103
        }
104
105 9
        $sizeDiff = ($newline ? 1 : 0);
106 9
        if (count($messages) + $sizeDiff > $this->terminal->getHeight()) {
107 2
            $messages = array_slice($messages, count($messages) + $sizeDiff - $this->terminal->getHeight());
108 2
        }
109
110 9
        $diff = $this->diff->lines($this->buffer, $messages);
111
112 9
        $output = $this->buildOutput($diff, $newline);
113 9
        $this->buffer = $messages;
114
115 9
        $this->output->write($output, $newline, $outputOptions);
116 9
    }
117
118
    /**
119
     * @param string|string[] $messages The message as an array of lines or a single string
120
     * @param int             $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered
121
     *                                  the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
122
     *
123
     * @return \string[]
124
     */
125 13
    private function format($messages, $options = 0)
126
    {
127 13
        $messages = (array) $messages;
128
129 13
        $types = self::OUTPUT_NORMAL | self::OUTPUT_RAW | self::OUTPUT_PLAIN;
130 13
        $type = $types & $options ?: self::OUTPUT_NORMAL;
131
132 13
        $verbosities = self::VERBOSITY_QUIET | self::VERBOSITY_NORMAL | self::VERBOSITY_VERBOSE | self::VERBOSITY_VERY_VERBOSE | self::VERBOSITY_DEBUG;
133 13
        $verbosity = $verbosities & $options ?: self::VERBOSITY_NORMAL;
134
135 13
        if ($verbosity > $this->getVerbosity()) {
136 1
            return [];
137
        }
138
139 12
        $formatter = $this->output->getFormatter();
140
141
        return array_map(function ($message) use ($type, $formatter) {
142
            switch ($type) {
143 12
                case OutputInterface::OUTPUT_NORMAL:
144 12
                    return $formatter->format($message);
145
                case OutputInterface::OUTPUT_PLAIN:
146
                    return strip_tags($formatter->format($message));
147
                case OutputInterface::OUTPUT_RAW:
148
                default:
149
                    return $message;
150
            }
151 12
        }, $messages);
152
    }
153
154
    /**
155
     * @param string|string[] $messages
156
     *
157
     * @return string[]
158
     */
159 12
    private function splitNewLines($messages)
160
    {
161
        $exploded = array_map(function ($line) {
162 12
            return explode("\n", $line);
163 12
        }, (array) $messages);
164 12
        $out = [];
165 12
        array_walk_recursive($exploded, function ($a) use (&$out) {
166 12
            $out[] = $a;
167 12
        });
168 12
        return $out;
169
    }
170
171
    /**
172
     * @return TerminalInterface
173
     */
174 1
    public function getTerminal()
175
    {
176 1
        return $this->terminal;
177
    }
178
179
    /**
180
     * @param array $diff
181
     * @param bool  $newline
182
     *
183
     * @return string
184
     */
185 9
    private function buildOutput(array $diff, $newline = false)
186
    {
187 9
        $buffer = '';
188
189
        // reset cursor position
190 9
        $count = count($this->buffer);
191 9
        $mod = ($newline ? 1 : 0);
192 9
        $up = ($count > 0 ? $count - 1 + $mod : $mod);
193 9
        if ($up > 0) {
194 8
            $buffer .= $this->terminal->moveUp($up);
195 8
        }
196 9
        $buffer .= "\r";
197
198 9
        $diffSize = count($diff);
199
200 9
        for ($i = 0; $i < $diffSize; $i++) {
201 9
            $d = $diff[$i];
202 9
            if ($i !== 0) {
203 8
                $buffer .= PHP_EOL . "\r";
204 8
            }
205 9
            if (!is_null($d)) {
206 9
                if ($d['col'] > 0) {
207 8
                    $buffer .= $this->terminal->moveRight($d['col']);
208 8
                }
209 9
                $buffer .= $this->terminal->eraseToEnd() . $d['str'];
210 9
            }
211 9
        }
212
213 9
        return $this->terminal->hideCursor() . $buffer . $this->terminal->showCursor();
214
    }
215
216
    /**
217
     * Writes a message to the output and adds a newline at the end.
218
     *
219
     * @param string|array $messages The message as an array of lines of a single string
220
     * @param int          $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered
221
     *                               the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
222
     */
223 1
    public function writeln($messages, $options = 0)
224
    {
225 1
        $this->write($messages, true, $options);
226 1
    }
227
228
    /**
229
     * Sets the verbosity of the output.
230
     *
231
     * @param int $level The level of verbosity (one of the VERBOSITY constants)
232
     */
233 1
    public function setVerbosity($level)
234
    {
235 1
        $this->output->setVerbosity($level);
236 1
    }
237
238
    /**
239
     * Gets the current verbosity of the output.
240
     *
241
     * @return int The current level of verbosity (one of the VERBOSITY constants)
242
     */
243 14
    public function getVerbosity()
244
    {
245 14
        return $this->output->getVerbosity();
246
    }
247
248
    /**
249
     * Returns whether verbosity is quiet (-q).
250
     *
251
     * @return bool true if verbosity is set to VERBOSITY_QUIET, false otherwise
252
     */
253 1
    public function isQuiet()
254
    {
255 1
        return $this->output->isQuiet();
256
    }
257
258
    /**
259
     * Returns whether verbosity is verbose (-v).
260
     *
261
     * @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise
262
     */
263 1
    public function isVerbose()
264
    {
265 1
        return $this->output->isVerbose();
266
    }
267
268
    /**
269
     * Returns whether verbosity is very verbose (-vv).
270
     *
271
     * @return bool true if verbosity is set to VERBOSITY_VERY_VERBOSE, false otherwise
272
     */
273 1
    public function isVeryVerbose()
274
    {
275 1
        return $this->output->isVeryVerbose();
276
    }
277
278
    /**
279
     * Returns whether verbosity is debug (-vvv).
280
     *
281
     * @return bool true if verbosity is set to VERBOSITY_DEBUG, false otherwise
282
     */
283 1
    public function isDebug()
284
    {
285 1
        return $this->output->isDebug();
286
    }
287
288
    /**
289
     * Sets the decorated flag.
290
     *
291
     * @param bool $decorated Whether to decorate the messages
292
     */
293 1
    public function setDecorated($decorated)
294
    {
295 1
        $this->output->setDecorated($decorated);
296 1
    }
297
298
    /**
299
     * Gets the decorated flag.
300
     *
301
     * @return bool true if the output will decorate messages, false otherwise
302
     */
303 1
    public function isDecorated()
304
    {
305 1
        return $this->output->isDecorated();
306
    }
307
308
    /**
309
     * Sets output formatter.
310
     *
311
     * @param OutputFormatterInterface $formatter
312
     */
313 1
    public function setFormatter(OutputFormatterInterface $formatter)
314
    {
315 1
        $this->output->setFormatter($formatter);
316 1
    }
317
318
    /**
319
     * Returns current output formatter instance.
320
     *
321
     * @return OutputFormatterInterface
322
     */
323 1
    public function getFormatter()
324
    {
325 1
        return $this->output->getFormatter();
326
    }
327
328
    /**
329
     * @return bool
330
     */
331 1
    public function isTrim()
332
    {
333 1
        return $this->trim;
334
    }
335
336
    /**
337
     * Should we wrap the input or not, if this is set to false, it will trim each line
338
     *
339
     * @param bool $trim
340
     *
341
     * @return $this
342
     */
343 2
    public function setTrim($trim)
344
    {
345 2
        $this->trim = $trim;
346 2
        return $this;
347
    }
348
}
349