Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 148 additions & 11 deletions cli/src/vine_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ use std::{
env,
ffi::OsStr,
fmt, fs,
io::{self, IsTerminal, Read, Write, stderr, stdin, stdout},
io::{self, IsTerminal, Read, StdoutLock, Write, stderr, stdin, stdout},
path::{Path, PathBuf},
process::exit,
process::{Command, Stdio, exit},
sync::mpsc,
thread,
time::{Duration, Instant},
};

Expand All @@ -33,10 +34,14 @@ use vine::{
backend::{BackendConfig, Target, backend, vi_to_ivm},
compiler::Compiler,
components::loader::{FileId, Loader, RealFS},
structures::{ast::Ident, resolutions::FragmentId},
structures::{
ast::Ident,
content::{Color, Element, Length, Output, Surround, Writer},
resolutions::FragmentId,
},
tools::{
doc::document,
fmt::Formatter,
fmt::{DEFAULT_MAX_WIDTH, Formatter},
repl::{self, Repl},
},
};
Expand All @@ -55,6 +60,7 @@ pub enum VineCommand {
/// Run tests in a Vine program
Test(VineTestCommand),
Fmt(VineFmtCommand),
Show(VineShowCommand),
Repl(VineReplCommand),
Lsp(VineLspCommand),

Expand All @@ -74,6 +80,7 @@ impl VineCommand {
VineCommand::Test(test) => test.execute(),
VineCommand::Repl(repl) => repl.execute(),
VineCommand::Fmt(fmt) => fmt.execute(),
VineCommand::Show(show) => show.execute(),
VineCommand::Lsp(lsp) => lsp.execute(),
VineCommand::Completion(completion) => completion.execute(),
VineCommand::Doc(doc) => doc.execute(),
Expand Down Expand Up @@ -493,24 +500,26 @@ impl VineReplCommand {
#[derive(Debug, Args)]
pub struct VineFmtCommand {
files: Option<Vec<PathBuf>>,
#[arg(long, default_value_t = DEFAULT_MAX_WIDTH)]
max_width: u16,
}

impl VineFmtCommand {
pub fn execute(self) -> Result<()> {
let mut success = true;
if let Some(files) = self.files {
if let Some(files) = &self.files {
for path in files {
let src = fs::read_to_string(&path)?;
if let Some(out) = Self::fmt(path.display(), &src) {
fs::write(&path, out)?;
let src = fs::read_to_string(path)?;
if let Some(out) = self.fmt(path.display(), &src) {
fs::write(path, out)?;
} else {
success = false;
}
}
} else {
let mut src = String::new();
stdin().read_to_string(&mut src)?;
if let Some(out) = Self::fmt("input", &src) {
if let Some(out) = self.fmt("input", &src) {
print!("{out}");
} else {
success = false;
Expand All @@ -522,8 +531,8 @@ impl VineFmtCommand {
Ok(())
}

fn fmt(path: impl fmt::Display, src: &str) -> Option<String> {
match Formatter::fmt(src) {
fn fmt(&self, path: impl fmt::Display, src: &str) -> Option<String> {
match Formatter::fmt(src, self.max_width) {
Ok(out) => Some(out),
Err(diag) => {
eprintln!("error formatting {path}:\n{diag}");
Expand All @@ -533,6 +542,134 @@ impl VineFmtCommand {
}
}

#[derive(Debug, Args)]
pub struct VineShowCommand {
file: PathBuf,
#[arg(long, hide = true)]
watch: bool,
}

impl VineShowCommand {
pub fn execute(self) -> Result<()> {
let path = &self.file;
let src = fs::read_to_string(path)?;
let mut content = match Formatter::_fmt(&src) {
Ok(content) => content,
Err(diag) => {
eprintln!("parse error:\n{diag}");
exit(1);
}
};

if self.watch {
let mut last_size = (0, 0);
loop {
thread::sleep(Duration::from_millis(10));
let (width, height) = (tput("cols") - 1, tput("lines"));
if last_size == (width, height) {
continue;
}
last_size = (width, height);

print!("\x1b[3J\x1b[H\x1b[2J\x1b[?7l");
content.format(
&mut Writer::new(Length(width), &mut Windowed::new(width, height)),
Surround::EMPTY,
);
}
} else {
let width = tput("cols");
content.format(&mut Writer::new(Length(width), &mut Stdout::new()), Surround::EMPTY);
}

return Ok(());

fn tput(var: &str) -> u16 {
let output = Command::new("tput").arg(var).stderr(Stdio::inherit()).output().unwrap();
String::from_utf8(output.stdout).unwrap().trim().parse().unwrap()
}

struct Stdout {
stdout: StdoutLock<'static>,
}

impl Stdout {
fn new() -> Self {
Stdout { stdout: stdout().lock() }
}
}

impl Output for Stdout {
fn line(&mut self) {
writeln!(self.stdout).unwrap();
}

fn write_char(&mut self, Color(r, g, b): Color, char: char) {
write!(self.stdout, "\x1b[38;2;{r};{g};{b}m{char}").unwrap();
}

fn write(&mut self, Color(r, g, b): Color, str: &str) {
write!(self.stdout, "\x1b[38;2;{r};{g};{b}m{str}").unwrap();
}
}

struct Windowed {
stdout: Stdout,
width: u16,
height: u16,
x: u16,
y: u16,
}

impl Windowed {
fn new(width: u16, height: u16) -> Self {
Windowed { stdout: Stdout::new(), width, height, x: 0, y: 0 }
}

fn write_char(&mut self, char: char) {
self.x += 1;
if self.y >= self.height || self.x > self.width {
return;
}
write!(self.stdout.stdout, "{}", char).unwrap();
}
}

impl Output for Windowed {
fn line(&mut self) {
if self.y >= self.height {
return;
}
for _ in self.x..self.width {
self.stdout.write(Color::NORMAL, " ");
}
if self.x <= self.width {
self.stdout.write(Color::COMMENT, "╎");
} else {
self.stdout.write(Color::ERROR, "×");
}
self.x = 0;
self.y += 1;
if self.y < self.height {
self.stdout.line();
}
}

fn write_char(&mut self, color: Color, char: char) {
self.stdout.write(color, "");
self.write_char(char);
}

fn write(&mut self, color: Color, str: &str) {
self.stdout.write(color, "");
for char in str.chars() {
self.write_char(char);
}
}
}
}
}

#[derive(Debug, Args)]
pub struct VineLspCommand {
#[command(flatten)]
Expand Down
4 changes: 4 additions & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"peekable",
"polyformic",
"primenesses",
"punct",
"readback",
"rels",
"Remez",
Expand All @@ -50,15 +51,18 @@
"rustyline",
"scrutinee",
"specializer",
"subarray",
"subimpls",
"subpattern",
"tarjan's",
"tput",
"tripterygium",
"underflows",
"uninit",
"unpair",
"unpark",
"unshifted",
"unspaced",
"untuple",
"vecs",
"visitee",
Expand Down
4 changes: 2 additions & 2 deletions lsp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use vine::{
checkpoint::Checkpoint,
diag::Diag,
},
tools::fmt::Formatter,
tools::fmt::{DEFAULT_MAX_WIDTH, Formatter},
};
use vine_util::idx::IdxVec;

Expand Down Expand Up @@ -399,7 +399,7 @@ impl LanguageServer for Backend {
}
};

let Ok(formatted) = Formatter::fmt(&src) else {
let Ok(formatted) = Formatter::fmt(&src, DEFAULT_MAX_WIDTH) else {
return Ok(None);
};
if formatted == src {
Expand Down
54 changes: 17 additions & 37 deletions root/IO/File.vi
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,11 @@ pub mod File {

/// Reads the next byte in the file, returning `None()` on EOF.
pub fn .read_byte(&self: &File, &io: &IO) -> Result[Option[N32], Error] {
let result: Ext = safe $ivy { ${io} = vi:ext#2[root:io:file:read_byte](
${self}
^
${~io}
${~self}
) };
let result: Ext = safe $ivy {
${io} = vi:ext#2[root:io:file:read_byte](${self} ^ ${~io} ${~self})
};
let byte = (result as Result[N32, Error]).try;
Ok(if byte == N32::maximum {
None()
} else {
Some(byte)
})
Ok(if byte == N32::maximum { None() } else { Some(byte) })
}

/// Reads all remaining data in the file into a `String`.
Expand All @@ -71,18 +64,11 @@ pub mod File {
/// sequence of bytes are not a valid UTF-8 character, the UTF-8 replacement
/// character is returned.
pub fn .read_char(&self: &File, &io: &IO) -> Result[Option[Char], Error] {
let result: Ext = safe $ivy { ${io} = vi:ext#2[root:io:file:read_char](
${self}
^
${~io}
${~self}
) };
let result: Ext = safe $ivy {
${io} = vi:ext#2[root:io:file:read_char](${self} ^ ${~io} ${~self})
};
let char = (result as Result[Char, Error]).try;
Ok(if char == N32::maximum as Char {
None()
} else {
Some(char)
})
Ok(if char == N32::maximum as Char { None() } else { Some(char) })
}

/// Writes the list of bytes into the file.
Expand All @@ -96,13 +82,9 @@ pub mod File {

/// Writes a byte to the file.
pub fn .write_byte(&self: &File, &io: &IO, byte: N32) -> Result[(), Error] {
let result: Ext = safe $ivy { ${io} = vi:ext#3[root:io:file:write_byte](
${self}
${byte}
^
${~io}
${~self}
) };
let result: Ext = safe $ivy {
${io} = vi:ext#3[root:io:file:write_byte](${self} ${byte} ^ ${~io} ${~self})
};
result as Result[(), Error]
}

Expand All @@ -117,13 +99,9 @@ pub mod File {

/// Writes a char to the file.
pub fn .write_char(&self: &File, &io: &IO, char: Char) -> Result[(), Error] {
let result: Ext = safe $ivy { ${io} = vi:ext#3[root:io:file:write_char](
${self}
${char}
^
${~io}
${~self}
) };
let result: Ext = safe $ivy {
${io} = vi:ext#3[root:io:file:write_char](${self} ${char} ^ ${~io} ${~self})
};
result as Result[(), Error]
}

Expand Down Expand Up @@ -176,7 +154,9 @@ pub mod File {
}

pub fn .message(&self: &ExtError) -> String {
let message: Ext[String] = safe $ivy { ${self} = vi:ext#1[root:io:error:message](^ ${~self}) };
let message: Ext[String] = safe $ivy {
${self} = vi:ext#1[root:io:error:message](^ ${~self})
};
message as String
}

Expand Down
Loading
Loading