diff --git a/js/deno.ts b/js/deno.ts index 1cc0a33d11..42bd380130 100644 --- a/js/deno.ts +++ b/js/deno.ts @@ -1,7 +1,7 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. // Public deno module. -export { pid, env, exit } from "./os"; +export { pid, env, exit, isTTY } from "./os"; export { chdir, cwd } from "./dir"; export { File, diff --git a/js/os.ts b/js/os.ts index 61b6ed9741..3122830cfa 100644 --- a/js/os.ts +++ b/js/os.ts @@ -21,6 +21,23 @@ interface CodeInfo { sourceCode: string | undefined; } +/** Check if running in terminal. + * + * import { isTTY } from "deno"; + * console.log(isTTY().stdout); + */ +export function isTTY(): { stdin: boolean; stdout: boolean; stderr: boolean } { + const builder = flatbuffers.createBuilder(); + msg.IsTTY.startIsTTY(builder); + const inner = msg.IsTTY.endIsTTY(builder); + const baseRes = sendSync(builder, msg.Any.IsTTY, inner)!; + assert(msg.Any.IsTTYRes === baseRes.innerType()); + const res = new msg.IsTTYRes(); + assert(baseRes.inner(res) != null); + + return { stdin: res.stdin(), stdout: res.stdout(), stderr: res.stderr() }; +} + /** Exit the Deno process with optional exit code. */ export function exit(exitCode = 0): never { const builder = flatbuffers.createBuilder(); diff --git a/js/os_test.ts b/js/os_test.ts index 21ec5e69d0..0784fd5e40 100644 --- a/js/os_test.ts +++ b/js/os_test.ts @@ -27,3 +27,8 @@ test(function osPid() { console.log("pid", deno.pid); assert(deno.pid > 0); }); + +// See complete tests in tools/is_tty_test.py +test(function osIsTTYSmoke() { + console.log(deno.isTTY()); +}); diff --git a/src/msg.fbs b/src/msg.fbs index 0bbc18b1d8..d7e71ab14f 100644 --- a/src/msg.fbs +++ b/src/msg.fbs @@ -61,7 +61,9 @@ union Any { RunStatus, RunStatusRes, Now, - NowRes + NowRes, + IsTTY, + IsTTYRes } enum ErrorKind: byte { @@ -483,4 +485,12 @@ table NowRes { time: uint64; } +table IsTTY {} + +table IsTTYRes { + stdin: bool; + stdout: bool; + stderr: bool; +} + root_type Base; diff --git a/src/ops.rs b/src/ops.rs index b17db87b21..b30473eb6d 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -1,4 +1,5 @@ // Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. + use crate::errors; use crate::errors::{DenoError, DenoResult, ErrorKind}; use crate::fs as deno_fs; @@ -18,6 +19,7 @@ use crate::resources::Resource; use crate::tokio_util; use crate::version; +use atty; use flatbuffers::FlatBufferBuilder; use futures; use futures::Async; @@ -121,6 +123,7 @@ pub fn dispatch( msg::Any::Write => op_write, msg::Any::WriteFile => op_write_file, msg::Any::Now => op_now, + msg::Any::IsTTY => op_is_tty, _ => panic!(format!( "Unhandled message {}", msg::enum_name_any(inner_type) @@ -197,6 +200,31 @@ fn op_now( )) } +fn op_is_tty( + _state: &Arc, + base: &msg::Base<'_>, + _data: libdeno::deno_buf, +) -> Box { + let builder = &mut FlatBufferBuilder::new(); + let inner = msg::IsTTYRes::create( + builder, + &msg::IsTTYResArgs { + stdin: atty::is(atty::Stream::Stdin), + stdout: atty::is(atty::Stream::Stdout), + stderr: atty::is(atty::Stream::Stderr), + }, + ); + ok_future(serialize_response( + base.cmd_id(), + builder, + msg::BaseArgs { + inner: Some(inner.as_union_value()), + inner_type: msg::Any::IsTTYRes, + ..Default::default() + }, + )) +} + fn op_exit( _config: &Arc, base: &msg::Base<'_>, diff --git a/tests/is_tty.ts b/tests/is_tty.ts new file mode 100644 index 0000000000..a571aee186 --- /dev/null +++ b/tests/is_tty.ts @@ -0,0 +1,2 @@ +import { isTTY } from "deno"; +console.log(isTTY().stdin); diff --git a/tools/is_tty_test.py b/tools/is_tty_test.py new file mode 100755 index 0000000000..218e7f6208 --- /dev/null +++ b/tools/is_tty_test.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. +import os +import pty +import select +import subprocess +from util import build_path, executable_suffix +from sys import stdin +from permission_prompt_test import tty_capture + +IS_TTY_TEST_TS = "tests/is_tty.ts" + +def is_tty_test(deno_exe): + cmd = [deno_exe, IS_TTY_TEST_TS] + code, stdout, _ = tty_capture(cmd, b'') + assert code == 0 + assert str(stdin.isatty()).lower() in stdout + +def main(): + deno_exe = os.path.join(build_path(), "deno" + executable_suffix) + is_tty_test(deno_exe) + +if __name__ == "__main__": + main() diff --git a/tools/test.py b/tools/test.py index 8da13b01a9..246b094b73 100755 --- a/tools/test.py +++ b/tools/test.py @@ -66,12 +66,14 @@ def main(argv): integration_tests(deno_exe) - # TODO We currently skip testing the prompt in Windows completely. + # TODO We currently skip testing the prompt and IsTTY in Windows completely. # Windows does not support the pty module used for testing the permission # prompt. if os.name != 'nt': from permission_prompt_test import permission_prompt_test + from is_tty_test import is_tty_test permission_prompt_test(deno_exe) + is_tty_test(deno_exe) repl_tests(deno_exe)