ninja_build/
line_printer.rs

1// Copyright 2013 Google Inc. All Rights Reserved.
2// Copyright 2017 The Ninja-rs Project Developers. All Rights Reserved.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use std::cell::{Cell, RefCell};
17
18#[derive(Clone, Copy, PartialEq)]
19pub enum LinePrinterLineType {
20    Full,
21    Elide,
22}
23
24#[cfg(not(windows))]
25struct LinePrinterOs {}
26
27#[cfg(not(windows))]
28impl LinePrinterOs {
29    fn new() -> Self {
30        LinePrinterOs {}
31    }
32
33    fn should_be_smart(&self) -> bool {
34        use libc;
35        use std::env;
36        use std::ffi;
37
38        if unsafe { libc::isatty(1usize as _) } != 0 {
39            return false;
40        }
41
42        if let Some(term) = env::var_os("TERM") {
43            if term != ffi::OsString::from("dumb") {
44                return true;
45            }
46        }
47        return false;
48    }
49}
50
51#[cfg(windows)]
52struct LinePrinterOs {
53    console: ::winapi::HANDLE,
54}
55
56#[cfg(windows)]
57impl LinePrinterOs {
58    fn new() -> Self {
59        use kernel32;
60        use winapi;
61        LinePrinterOs { console: unsafe { kernel32::GetStdHandle(winapi::STD_OUTPUT_HANDLE) } }
62    }
63
64    fn should_be_smart(&self) -> bool {
65        use kernel32;
66        use winapi;
67        use std::mem;
68
69        let mut csbi = unsafe { mem::zeroed::<winapi::CONSOLE_SCREEN_BUFFER_INFO>() };
70        unsafe {
71            kernel32::GetConsoleScreenBufferInfo(self.console, &mut csbi as *mut _) != winapi::FALSE
72        }
73    }
74}
75
76
77
78/// Prints lines of text, possibly overprinting previously printed lines
79/// if the terminal supports it.
80pub struct LinePrinter {
81    /// Whether we can do fancy terminal control codes.
82    smart_terminal: bool,
83
84    /// Whether the caret is at the beginning of a blank line.
85    have_blank_line: Cell<bool>,
86
87    /// Whether console is locked.
88    console_locked: bool,
89
90    /// Buffered current line while console is locked.
91    line_buffer: RefCell<Vec<u8>>,
92
93    /// Buffered line type while console is locked.
94    line_type: Cell<LinePrinterLineType>,
95
96    /// Buffered console output while console is locked.
97    output_buffer: RefCell<Vec<u8>>,
98
99    extra: LinePrinterOs,
100}
101
102impl LinePrinter {
103    pub fn new() -> Self {
104        let lp_os = LinePrinterOs::new();
105        let smart = lp_os.should_be_smart();
106
107        LinePrinter {
108            smart_terminal: smart,
109            have_blank_line: Cell::new(true),
110            console_locked: false,
111            line_buffer: RefCell::new(Vec::new()),
112            line_type: Cell::new(LinePrinterLineType::Full),
113            output_buffer: RefCell::new(Vec::new()),
114            extra: lp_os,
115        }
116    }
117
118    pub fn is_smart_terminal(&self) -> bool {
119        self.smart_terminal
120    }
121    pub fn set_smart_terminal(&mut self, smart: bool) {
122        self.smart_terminal = smart;
123    }
124
125    /// Overprints the current line. If type is ELIDE, elides to_print to fit on
126    /// one line.
127    pub fn print(&self, to_print: &[u8], line_type: LinePrinterLineType) {
128        if self.console_locked {
129            *self.line_buffer.borrow_mut() = to_print.to_owned();
130            self.line_type.set(line_type);
131            return;
132        }
133
134        if self.smart_terminal {
135            print!("\r"); // Print over previous line, if any.
136            // On Windows, calling a C library function writing to stdout also handles
137            // pausing the executable when the "Pause" key or Ctrl-S is pressed.
138        }
139
140        if self.smart_terminal && line_type == LinePrinterLineType::Elide {
141            /*
142#ifdef _WIN32
143    CONSOLE_SCREEN_BUFFER_INFO csbi;
144    GetConsoleScreenBufferInfo(console_, &csbi);
145
146    to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
147    // We don't want to have the cursor spamming back and forth, so instead of
148    // printf use WriteConsoleOutput which updates the contents of the buffer,
149    // but doesn't move the cursor position.
150    COORD buf_size = { csbi.dwSize.X, 1 };
151    COORD zero_zero = { 0, 0 };
152    SMALL_RECT target = {
153      csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
154      static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
155      csbi.dwCursorPosition.Y
156    };
157    vector<CHAR_INFO> char_data(csbi.dwSize.X);
158    for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
159      char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';
160      char_data[i].Attributes = csbi.wAttributes;
161    }
162    WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);
163#else
164    // Limit output to width of the terminal if provided so we don't cause
165    // line-wrapping.
166    winsize size;
167    if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
168      to_print = ElideMiddle(to_print, size.ws_col);
169    }
170    printf("%s", to_print.c_str());
171    printf("\x1B[K");  // Clear to end of line.
172    fflush(stdout);
173#endif
174
175    have_blank_line_ = false;
176*/
177            self.have_blank_line.set(false);
178
179            return;
180            unimplemented!();
181        } else {
182            use std::io::{self, Write};
183
184            let stdout = io::stdout();
185            let mut handle = stdout.lock();
186
187            let _ = handle.write(to_print);
188            let _ = handle.write(b"\n");
189        }
190    }
191
192    /// Print the given data to the console, or buffer it if it is locked.
193    fn print_or_buffer(&self, to_print: &[u8]) {
194        if self.console_locked {
195            self.output_buffer.borrow_mut().extend_from_slice(to_print);
196        } else {
197            use std::io::{self, Write};
198
199            let stdout = io::stdout();
200            let mut handle = stdout.lock();
201
202            let _ = handle.write(to_print);
203        }
204    }
205
206    /// Prints a string on a new line, not overprinting previous output.
207    pub fn print_on_new_line(&self, to_print: &[u8]) {
208        if self.console_locked && !self.line_buffer.borrow().is_empty() {
209            let mut line_buffer = self.line_buffer.borrow_mut();
210            let mut output_buffer = self.output_buffer.borrow_mut();
211            output_buffer.extend(line_buffer.drain(..));
212            output_buffer.push(b'\n');
213        }
214
215        if !self.have_blank_line.get() {
216            self.print_or_buffer(b"\n");
217        }
218        if !to_print.is_empty() {
219            self.print_or_buffer(to_print);
220        }
221        self.have_blank_line.set(match to_print.last() {
222            None | Some(&b'\n') => true,
223            _ => false,
224        });
225    }
226
227    /// Lock or unlock the console.  Any output sent to the LinePrinter while the
228    /// console is locked will not be printed until it is unlocked.
229    pub fn set_console_locked(&mut self, locked: bool) {
230        use std::mem;
231
232        if self.console_locked == locked {
233            return;
234        }
235
236        if locked {
237            self.print_on_new_line(b"");
238        }
239
240        self.console_locked = locked;
241
242        if !locked {
243            let mut output_buffer = Vec::new();
244            let mut line_buffer = Vec::new();
245            mem::swap(&mut output_buffer, &mut *self.output_buffer.borrow_mut());
246            mem::swap(&mut line_buffer, &mut *self.line_buffer.borrow_mut());
247            self.print_on_new_line(&output_buffer);
248            if !line_buffer.is_empty() {
249                self.print(&line_buffer, self.line_type.get());
250            }
251        }
252        return;
253    }
254}
255
256/*
257
258struct LinePrinter {
259  LinePrinter();
260
261  bool is_smart_terminal() const { return smart_terminal_; }
262  void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
263
264
265  /// Overprints the current line. If type is ELIDE, elides to_print to fit on
266  /// one line.
267  void Print(string to_print, LineType type);
268
269  /// Prints a string on a new line, not overprinting previous output.
270  void PrintOnNewLine(const string& to_print);
271
272  /// Lock or unlock the console.  Any output sent to the LinePrinter while the
273  /// console is locked will not be printed until it is unlocked.
274  void SetConsoleLocked(bool locked);
275
276 private:
277  /// Whether we can do fancy terminal control codes.
278  bool smart_terminal_;
279
280  /// Whether the caret is at the beginning of a blank line.
281  bool have_blank_line_;
282
283  /// Whether console is locked.
284  bool console_locked_;
285
286  /// Buffered current line while console is locked.
287  string line_buffer_;
288
289  /// Buffered line type while console is locked.
290  LineType line_type_;
291
292  /// Buffered console output while console is locked.
293  string output_buffer_;
294
295#ifdef _WIN32
296  void* console_;
297#endif
298
299  /// Print the given data to the console, or buffer it if it is locked.
300  void PrintOrBuffer(const char *data, size_t size);
301};
302
303#endif  // NINJA_LINE_PRINTER_H_
304
305*/