Completed
Push — master ( a4afb1...d2ff2c )
by Harry
02:25
created

DiffConsoleOutput::reWrite()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 8.5906
c 0
b 0
f 0
cc 5
eloc 14
nc 10
nop 3
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\ConsoleOutputInterface;
23
use Symfony\Component\Console\Output\OutputInterface;
24
25
/**
26
 * This takes an array of lines to write to the console, does a different and only over-writes what has changed to the
27
 * console
28
 */
29
class DiffConsoleOutput implements ConsoleOutputInterface
30
{
31
    /** @var string[] */
32
    private $buffer = [];
33
    /** @var ConsoleDiff */
34
    private $diff;
35
    /** @var TerminalInterface */
36
    private $terminal;
37
    /** @var ConsoleOutputInterface */
38
    private $output;
39
    /** @var Wrapper */
40
    private $wrapper;
41
    /** @var bool */
42
    private $trim = false;
43
44
    /**
45
     * Constructor.
46
     *
47
     * @param ConsoleOutputInterface $output
48
     * @param TerminalInterface      $terminal
49
     * @param Wrapper                $wrapper
50
     */
51
    public function __construct(
52
        ConsoleOutputInterface $output,
53
        TerminalInterface $terminal = null,
54
        Wrapper $wrapper = null
55
    ) {
56
        $this->output = $output;
57
        $this->diff = new ConsoleDiff();
58
        $this->terminal = $terminal ?: new Terminal();
59
        $this->wrapper = $wrapper ?: new Wrapper($this->terminal->getWidth());
60
    }
61
62
    /**
63
     * Sets information about the terminal
64
     *
65
     * @param TerminalInterface $terminal
66
     */
67
    public function setTerminal(TerminalInterface $terminal)
68
    {
69
        $this->terminal = $terminal;
70
        $this->wrapper->setWidth($terminal->getWidth());
71
    }
72
73
    /**
74
     * @return TerminalInterface
75
     */
76
    public function getTerminal()
77
    {
78
        return $this->terminal;
79
    }
80
81
    /**
82
     * @param string|array $messages The message as an array of lines or a single string
83
     * @param bool         $newline  Whether to add a newline
84
     * @param int          $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered
85
     *                               the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
86
     */
87
    public function write($messages, $newline = false, $options = 0)
88
    {
89
        $this->buffer = [];
90
        $this->output->write($messages, $newline, $options);
91
    }
92
93
    /**
94
     * @param string|string[] $messages The message as an array of lines or a single string
95
     * @param bool            $newline  Whether to add a newline
96
     * @param int             $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered
97
     *                                  the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
98
     */
99
    public function reWrite($messages, $newline = false, $options = 0)
100
    {
101
        $messages = (array) $messages;
102
        $messages = ($this->trim) ? $this->wrapper->trim($messages) : $this->wrapper->wrap($messages);
103
104
        if (count($this->buffer) === 0) {
105
            $this->buffer = $messages;
106
            $this->output->write($messages, $newline, $options);
107
            return;
108
        }
109
110
        $sizeDiff = ($newline ? 1 : 0);
111
        if (count($messages) + $sizeDiff > $this->terminal->getHeight()) {
112
            $messages = array_slice($messages, count($messages) + $sizeDiff - $this->terminal->getHeight());
113
        }
114
115
        $diff = $this->diff->lines($this->buffer, $messages);
116
117
        $output = $this->buildOutput($diff, $newline);
118
        $this->buffer = $messages;
119
120
        $this->output->write($output, $newline, $options);
121
    }
122
123
    /**
124
     * @param array $diff
125
     * @param bool  $newline
126
     *
127
     * @return string
128
     */
129
    private function buildOutput(array $diff, $newline = false)
130
    {
131
        $buffer = '';
132
133
        // reset cursor position
134
        $count = count($this->buffer);
135
        $mod = ($newline ? 1 : 0);
136
        $up = ($count > 0 ? $count - 1 + $mod : $mod);
137
        if ($up > 0) {
138
            $buffer .= $this->terminal->moveUp($up);
139
        }
140
        $buffer .= "\r";
141
142
        $diffSize = count($diff);
143
144
        for ($i = 0; $i < $diffSize; $i++) {
145
            $d = $diff[$i];
146
            if ($i !== 0) {
147
                $buffer .= PHP_EOL;
148
            }
149
            if (!is_null($d)) {
150
                if ($d['col'] > 0) {
151
                    $buffer .= $this->terminal->moveRight($d['col']);
152
                }
153
                $buffer .= $this->terminal->eraseToEnd() . $d['str'];
154
            }
155
        }
156
157
        return $buffer;
158
    }
159
160
    /**
161
     * Gets the OutputInterface for errors.
162
     *
163
     * @return OutputInterface
164
     */
165
    public function getErrorOutput()
166
    {
167
        return $this->output->getErrorOutput();
168
    }
169
170
    /**
171
     * Sets the OutputInterface used for errors.
172
     *
173
     * @param OutputInterface $error
174
     */
175
    public function setErrorOutput(OutputInterface $error)
176
    {
177
        $this->output->setErrorOutput($error);
178
    }
179
180
    /**
181
     * Writes a message to the output and adds a newline at the end.
182
     *
183
     * @param string|array $messages The message as an array of lines of a single string
184
     * @param int          $options  A bitmask of options (one of the OUTPUT or VERBOSITY constants), 0 is considered
185
     *                               the same as self::OUTPUT_NORMAL | self::VERBOSITY_NORMAL
186
     */
187
    public function writeln($messages, $options = 0)
188
    {
189
        $this->write($messages, true, $options);
190
    }
191
192
    /**
193
     * Sets the verbosity of the output.
194
     *
195
     * @param int $level The level of verbosity (one of the VERBOSITY constants)
196
     */
197
    public function setVerbosity($level)
198
    {
199
        $this->output->setVerbosity($level);
200
    }
201
202
    /**
203
     * Gets the current verbosity of the output.
204
     *
205
     * @return int The current level of verbosity (one of the VERBOSITY constants)
206
     */
207
    public function getVerbosity()
208
    {
209
        return $this->output->getVerbosity();
210
    }
211
212
    /**
213
     * Returns whether verbosity is quiet (-q).
214
     *
215
     * @return bool true if verbosity is set to VERBOSITY_QUIET, false otherwise
216
     */
217
    public function isQuiet()
218
    {
219
        return $this->output->isQuiet();
220
    }
221
222
    /**
223
     * Returns whether verbosity is verbose (-v).
224
     *
225
     * @return bool true if verbosity is set to VERBOSITY_VERBOSE, false otherwise
226
     */
227
    public function isVerbose()
228
    {
229
        return $this->output->isVerbose();
230
    }
231
232
    /**
233
     * Returns whether verbosity is very verbose (-vv).
234
     *
235
     * @return bool true if verbosity is set to VERBOSITY_VERY_VERBOSE, false otherwise
236
     */
237
    public function isVeryVerbose()
238
    {
239
        return $this->output->isVeryVerbose();
240
    }
241
242
    /**
243
     * Returns whether verbosity is debug (-vvv).
244
     *
245
     * @return bool true if verbosity is set to VERBOSITY_DEBUG, false otherwise
246
     */
247
    public function isDebug()
248
    {
249
        return $this->output->isDebug();
250
    }
251
252
    /**
253
     * Sets the decorated flag.
254
     *
255
     * @param bool $decorated Whether to decorate the messages
256
     */
257
    public function setDecorated($decorated)
258
    {
259
        $this->output->setDecorated($decorated);
260
    }
261
262
    /**
263
     * Gets the decorated flag.
264
     *
265
     * @return bool true if the output will decorate messages, false otherwise
266
     */
267
    public function isDecorated()
268
    {
269
        return $this->output->isDecorated();
270
    }
271
272
    /**
273
     * Sets output formatter.
274
     *
275
     * @param OutputFormatterInterface $formatter
276
     */
277
    public function setFormatter(OutputFormatterInterface $formatter)
278
    {
279
        $this->output->setFormatter($formatter);
280
    }
281
282
    /**
283
     * Returns current output formatter instance.
284
     *
285
     * @return OutputFormatterInterface
286
     */
287
    public function getFormatter()
288
    {
289
        return $this->output->getFormatter();
290
    }
291
292
    /**
293
     * @return bool
294
     */
295
    public function isTrim()
296
    {
297
        return $this->trim;
298
    }
299
300
    /**
301
     * Should we wrap the input or not, if this is set to false, it will trim each line
302
     *
303
     * @param bool $trim
304
     *
305
     * @return $this
306
     */
307
    public function setTrim($trim)
308
    {
309
        $this->trim = $trim;
310
        return $this;
311
    }
312
}
313