feat(unstable): add Deno.consoleSize (#6520)

This commit is contained in:
Sebastien Filion 2020-07-10 10:07:12 -04:00 committed by GitHub
parent dc6b3ef714
commit 1bcc35b84a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 115 additions and 3 deletions

View file

@ -12,7 +12,7 @@ export { openPlugin } from "./ops/plugins.ts";
export { transpileOnly, compile, bundle } from "./compiler_api.ts"; export { transpileOnly, compile, bundle } from "./compiler_api.ts";
export { applySourceMap, formatDiagnostics } from "./ops/errors.ts"; export { applySourceMap, formatDiagnostics } from "./ops/errors.ts";
export { signal, signals, Signal, SignalStream } from "./signals.ts"; export { signal, signals, Signal, SignalStream } from "./signals.ts";
export { setRaw } from "./ops/tty.ts"; export { setRaw, consoleSize } from "./ops/tty.ts";
export { utimeSync, utime } from "./ops/fs/utime.ts"; export { utimeSync, utime } from "./ops/fs/utime.ts";
export { ftruncateSync, ftruncate } from "./ops/fs/truncate.ts"; export { ftruncateSync, ftruncate } from "./ops/fs/truncate.ts";
export { shutdown, ShutdownMode } from "./net.ts"; export { shutdown, ShutdownMode } from "./net.ts";

View file

@ -43,6 +43,21 @@ declare namespace Deno {
* Requires `allow-read` and `allow-write` permissions. */ * Requires `allow-read` and `allow-write` permissions. */
export function link(oldpath: string, newpath: string): Promise<void>; export function link(oldpath: string, newpath: string): Promise<void>;
/** **UNSTABLE**: New API, yet to be vetted.
*
* Gets the size of the console as columns/rows.
*
* ```ts
* const { columns, rows } = await Deno.consoleSize(Deno.stdout.rid);
* ```
*/
export function consoleSize(
rid: number
): {
columns: number;
rows: number;
};
export type SymlinkOptions = { export type SymlinkOptions = {
type: "file" | "dir"; type: "file" | "dir";
}; };

View file

@ -2,6 +2,10 @@
import { sendSync } from "./dispatch_json.ts"; import { sendSync } from "./dispatch_json.ts";
export function consoleSize(rid: number): [number, number] {
return sendSync("op_console_size", { rid });
}
export function isatty(rid: number): boolean { export function isatty(rid: number): boolean {
return sendSync("op_isatty", { rid }); return sendSync("op_isatty", { rid });
} }

View file

@ -8,7 +8,7 @@ use deno_core::CoreIsolateState;
use deno_core::ZeroCopyBuf; use deno_core::ZeroCopyBuf;
#[cfg(unix)] #[cfg(unix)]
use nix::sys::termios; use nix::sys::termios;
use serde_derive::Deserialize; use serde_derive::{Deserialize, Serialize};
use serde_json::Value; use serde_json::Value;
#[cfg(windows)] #[cfg(windows)]
@ -38,6 +38,7 @@ fn get_windows_handle(
pub fn init(i: &mut CoreIsolate, s: &State) { pub fn init(i: &mut CoreIsolate, s: &State) {
i.register_op("op_set_raw", s.stateful_json_op2(op_set_raw)); i.register_op("op_set_raw", s.stateful_json_op2(op_set_raw));
i.register_op("op_isatty", s.stateful_json_op2(op_isatty)); i.register_op("op_isatty", s.stateful_json_op2(op_isatty));
i.register_op("op_console_size", s.stateful_json_op2(op_console_size));
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -250,3 +251,80 @@ pub fn op_isatty(
})?; })?;
Ok(JsonOp::Sync(json!(isatty))) Ok(JsonOp::Sync(json!(isatty)))
} }
#[derive(Deserialize)]
struct ConsoleSizeArgs {
rid: u32,
}
#[derive(Serialize)]
struct ConsoleSize {
columns: u32,
rows: u32,
}
pub fn op_console_size(
isolate_state: &mut CoreIsolateState,
state: &State,
args: Value,
_zero_copy: &mut [ZeroCopyBuf],
) -> Result<JsonOp, OpError> {
state.check_unstable("Deno.consoleSize");
let args: ConsoleSizeArgs = serde_json::from_value(args)?;
let rid = args.rid;
let mut resource_table = isolate_state.resource_table.borrow_mut();
let size =
std_file_resource(&mut resource_table, rid as u32, move |r| match r {
Ok(std_file) => {
#[cfg(windows)]
{
use std::os::windows::io::AsRawHandle;
let handle = std_file.as_raw_handle();
unsafe {
let mut bufinfo: winapi::um::wincon::CONSOLE_SCREEN_BUFFER_INFO =
std::mem::zeroed();
if winapi::um::wincon::GetConsoleScreenBufferInfo(
handle,
&mut bufinfo,
) == 0
{
// TODO (caspervonb) use GetLastError
return Err(OpError::other(
winapi::um::errhandlingapi::GetLastError().to_string(),
));
}
Ok(ConsoleSize {
columns: bufinfo.dwSize.X as u32,
rows: bufinfo.dwSize.Y as u32,
})
}
}
#[cfg(unix)]
{
use std::os::unix::io::AsRawFd;
let fd = std_file.as_raw_fd();
unsafe {
let mut size: libc::winsize = std::mem::zeroed();
if libc::ioctl(fd, libc::TIOCGWINSZ, &mut size as *mut _) != 0 {
return Err(OpError::from(std::io::Error::last_os_error()));
}
// TODO (caspervonb) return a tuple instead
Ok(ConsoleSize {
columns: size.ws_col as u32,
rows: size.ws_row as u32,
})
}
}
}
Err(_) => Err(OpError::bad_resource_id()),
})?;
Ok(JsonOp::Sync(json!(size)))
}

View file

@ -1,8 +1,23 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
import { unitTest, assert } from "./test_util.ts"; import { unitTest, assert, assertThrows } from "./test_util.ts";
// Note tests for Deno.setRaw is in integration tests. // Note tests for Deno.setRaw is in integration tests.
unitTest({ perms: { read: true } }, function consoleSizeFile(): void {
const file = Deno.openSync("cli/tests/hello.txt");
assertThrows(() => {
Deno.consoleSize(file.rid);
}, Error);
file.close();
});
unitTest(function consoleSizeError(): void {
assertThrows(() => {
// Absurdly large rid.
Deno.consoleSize(0x7fffffff);
}, Deno.errors.BadResource);
});
unitTest({ perms: { read: true } }, function isatty(): void { unitTest({ perms: { read: true } }, function isatty(): void {
// CI not under TTY, so cannot test stdin/stdout/stderr. // CI not under TTY, so cannot test stdin/stdout/stderr.
const f = Deno.openSync("cli/tests/hello.txt"); const f = Deno.openSync("cli/tests/hello.txt");