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*/