aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-external-packages/src/symfony/console/cursor.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-12 01:01:35 +0900
committernsfisis <nsfisis@gmail.com>2026-06-12 01:01:43 +0900
commit1e44f5723e4c0e0903d00a61f254901e612fe5e1 (patch)
tree9c2e1eec364bbf6418a4072ee7767b04c3df0297 /crates/shirabe-external-packages/src/symfony/console/cursor.rs
parentddf0a624145b618c05c2ee47414545b99b685f37 (diff)
downloadphp-shirabe-1e44f5723e4c0e0903d00a61f254901e612fe5e1.tar.gz
php-shirabe-1e44f5723e4c0e0903d00a61f254901e612fe5e1.tar.zst
php-shirabe-1e44f5723e4c0e0903d00a61f254901e612fe5e1.zip
feat(symfony-console): port Symfony Console and make the workspace compile
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-external-packages/src/symfony/console/cursor.rs')
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/cursor.rs259
1 files changed, 259 insertions, 0 deletions
diff --git a/crates/shirabe-external-packages/src/symfony/console/cursor.rs b/crates/shirabe-external-packages/src/symfony/console/cursor.rs
new file mode 100644
index 0000000..9016fab
--- /dev/null
+++ b/crates/shirabe-external-packages/src/symfony/console/cursor.rs
@@ -0,0 +1,259 @@
+use crate::symfony::console::output::OutputInterface;
+use crate::symfony::console::output::output_interface;
+use std::cell::RefCell;
+use std::rc::Rc;
+
+#[derive(Debug)]
+pub struct Cursor {
+ output: Rc<RefCell<dyn OutputInterface>>,
+ input: shirabe_php_shim::PhpMixed,
+}
+
+impl Cursor {
+ pub fn new(
+ output: Rc<RefCell<dyn OutputInterface>>,
+ input: Option<shirabe_php_shim::PhpMixed>,
+ ) -> Self {
+ let input = input.unwrap_or_else(|| {
+ if shirabe_php_shim::defined("STDIN") {
+ // PHP uses the `STDIN` resource constant here, but the shim models it as
+ // `PhpResource` while this field is `PhpMixed` (which has no resource
+ // variant). Fall back to opening `php://input` so the value stays a
+ // PhpMixed stream handle.
+ shirabe_php_shim::fopen("php://input", "r+")
+ } else {
+ shirabe_php_shim::fopen("php://input", "r+")
+ }
+ });
+
+ Self { output, input }
+ }
+
+ pub fn move_up(&self, lines: i64) -> &Self {
+ self.output.borrow().write(
+ &[shirabe_php_shim::sprintf(
+ "\x1b[%dA",
+ &[shirabe_php_shim::PhpMixed::Int(lines)],
+ )],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ pub fn move_down(&self, lines: i64) -> &Self {
+ self.output.borrow().write(
+ &[shirabe_php_shim::sprintf(
+ "\x1b[%dB",
+ &[shirabe_php_shim::PhpMixed::Int(lines)],
+ )],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ pub fn move_right(&self, columns: i64) -> &Self {
+ self.output.borrow().write(
+ &[shirabe_php_shim::sprintf(
+ "\x1b[%dC",
+ &[shirabe_php_shim::PhpMixed::Int(columns)],
+ )],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ pub fn move_left(&self, columns: i64) -> &Self {
+ self.output.borrow().write(
+ &[shirabe_php_shim::sprintf(
+ "\x1b[%dD",
+ &[shirabe_php_shim::PhpMixed::Int(columns)],
+ )],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ pub fn move_to_column(&self, column: i64) -> &Self {
+ self.output.borrow().write(
+ &[shirabe_php_shim::sprintf(
+ "\x1b[%dG",
+ &[shirabe_php_shim::PhpMixed::Int(column)],
+ )],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ pub fn move_to_position(&self, column: i64, row: i64) -> &Self {
+ self.output.borrow().write(
+ &[shirabe_php_shim::sprintf(
+ "\x1b[%d;%dH",
+ &[
+ shirabe_php_shim::PhpMixed::Int(row + 1),
+ shirabe_php_shim::PhpMixed::Int(column),
+ ],
+ )],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ pub fn save_position(&self) -> &Self {
+ self.output.borrow().write(
+ &["\x1b7".to_string()],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ pub fn restore_position(&self) -> &Self {
+ self.output.borrow().write(
+ &["\x1b8".to_string()],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ pub fn hide(&self) -> &Self {
+ self.output.borrow().write(
+ &["\x1b[?25l".to_string()],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ pub fn show(&self) -> &Self {
+ self.output.borrow().write(
+ &["\x1b[?25h\x1b[?0c".to_string()],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ /// Clears all the output from the current line.
+ pub fn clear_line(&self) -> &Self {
+ self.output.borrow().write(
+ &["\x1b[2K".to_string()],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ /// Clears all the output from the current line after the current position.
+ pub fn clear_line_after(&self) -> &Self {
+ self.output.borrow().write(
+ &["\x1b[K".to_string()],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ /// Clears all the output from the cursors' current position to the end of the screen.
+ pub fn clear_output(&self) -> &Self {
+ self.output.borrow().write(
+ &["\x1b[0J".to_string()],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ /// Clears the entire screen.
+ pub fn clear_screen(&self) -> &Self {
+ self.output.borrow().write(
+ &["\x1b[2J".to_string()],
+ false,
+ output_interface::OUTPUT_NORMAL,
+ );
+
+ self
+ }
+
+ /// Returns the current cursor position as x,y coordinates.
+ pub fn get_current_position(&self) -> Vec<i64> {
+ static IS_TTY_SUPPORTED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
+
+ let is_tty_supported = if shirabe_php_shim::function_exists("proc_open") {
+ *IS_TTY_SUPPORTED.get_or_init(|| {
+ let mut pipes = shirabe_php_shim::PhpMixed::Null;
+ shirabe_php_shim::proc_open(
+ "echo 1 >/dev/null",
+ &vec![
+ shirabe_php_shim::PhpMixed::List(vec![
+ Box::new(shirabe_php_shim::PhpMixed::String("file".to_string())),
+ Box::new(shirabe_php_shim::PhpMixed::String("/dev/tty".to_string())),
+ Box::new(shirabe_php_shim::PhpMixed::String("r".to_string())),
+ ]),
+ shirabe_php_shim::PhpMixed::List(vec![
+ Box::new(shirabe_php_shim::PhpMixed::String("file".to_string())),
+ Box::new(shirabe_php_shim::PhpMixed::String("/dev/tty".to_string())),
+ Box::new(shirabe_php_shim::PhpMixed::String("w".to_string())),
+ ]),
+ shirabe_php_shim::PhpMixed::List(vec![
+ Box::new(shirabe_php_shim::PhpMixed::String("file".to_string())),
+ Box::new(shirabe_php_shim::PhpMixed::String("/dev/tty".to_string())),
+ Box::new(shirabe_php_shim::PhpMixed::String("w".to_string())),
+ ]),
+ ],
+ &mut pipes,
+ )
+ })
+ } else {
+ false
+ };
+
+ if !is_tty_supported {
+ return vec![1, 1];
+ }
+
+ let stty_mode = shirabe_php_shim::shell_exec("stty -g");
+ shirabe_php_shim::shell_exec("stty -icanon -echo");
+
+ shirabe_php_shim::fwrite(self.input.clone(), "\x1b[6n", 0);
+
+ let code = shirabe_php_shim::trim(
+ shirabe_php_shim::fread(self.input.clone(), 1024)
+ .as_deref()
+ .unwrap_or(""),
+ None,
+ );
+
+ shirabe_php_shim::shell_exec(&shirabe_php_shim::sprintf(
+ "stty %s",
+ &[shirabe_php_shim::PhpMixed::String(
+ stty_mode.unwrap_or_default(),
+ )],
+ ));
+
+ let mut row: i64 = 0;
+ let mut col: i64 = 0;
+ shirabe_php_shim::sscanf(&code, "\x1b[%d;%dR", &mut row, &mut col);
+
+ vec![col, row]
+ }
+}