From 3163f33887b0826b54524b0f00464e5aecdb3b14 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Wed, 8 Mar 2023 19:23:47 +0100 Subject: [PATCH] Add support for shebang at the top of a script --- CHANGELOG.md | 12 ++++++++++++ packages/cli/src/cli.rs | 4 ++-- packages/cli/src/utils/files.rs | 18 ++++++++++++++++++ packages/cli/src/utils/listing.rs | 5 +++-- 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef69fd7..c5d4504 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added + +- Added support for shebangs at the top of a script, meaning scripts such as this one will now run without throwing a syntax error: + + ```lua + #!/usr/bin/env lune + + print("Hello, world!") + ``` + ## `0.5.5` - March 8th, 2023 ### Added diff --git a/packages/cli/src/cli.rs b/packages/cli/src/cli.rs index 97fe11b..3c0723a 100644 --- a/packages/cli/src/cli.rs +++ b/packages/cli/src/cli.rs @@ -15,7 +15,7 @@ use crate::{ generate_selene_defs_from_definitions, generate_wiki_dir_from_definitions, }, utils::{ - files::discover_script_file_path_including_lune_dirs, + files::{discover_script_file_path_including_lune_dirs, strip_shebang}, listing::{find_lune_scripts, print_lune_scripts, sort_lune_scripts}, }, }; @@ -177,7 +177,7 @@ impl Cli { // Create a new lune object with all globals & run the script let result = Lune::new() .with_args(self.script_args) - .try_run(&script_display_name, &script_contents) + .try_run(&script_display_name, strip_shebang(script_contents)) .await; Ok(match result { Err(err) => { diff --git a/packages/cli/src/utils/files.rs b/packages/cli/src/utils/files.rs index 88f4796..d7548a5 100644 --- a/packages/cli/src/utils/files.rs +++ b/packages/cli/src/utils/files.rs @@ -157,3 +157,21 @@ pub fn parse_lune_description_from_file(contents: &str) -> Option { Some(unindented_lines) } } + +pub fn strip_shebang(mut contents: Vec) -> Vec { + if contents.starts_with(b"#!") { + if let Some(first_newline_idx) = + contents + .iter() + .enumerate() + .find_map(|(idx, c)| if *c == b'\n' { Some(idx) } else { None }) + { + // NOTE: We keep the newline here on purpose to preserve + // correct line numbers in stack traces, the only reason + // we strip the shebang is to get the lua script to parse + // and the extra newline is not really a problem for that + contents.drain(..first_newline_idx); + } + } + contents +} diff --git a/packages/cli/src/utils/listing.rs b/packages/cli/src/utils/listing.rs index 307c980..23d43c8 100644 --- a/packages/cli/src/utils/listing.rs +++ b/packages/cli/src/utils/listing.rs @@ -23,16 +23,17 @@ pub async fn find_lune_scripts() -> Result> { while let Some(entry) = dir.next_entry().await? { let meta = entry.metadata().await?; if meta.is_file() { - let contents = fs::read_to_string(entry.path()).await?; + let contents = fs::read(entry.path()).await?; files.push((entry, meta, contents)); } } let parsed: Vec<_> = files .iter() .map(|(entry, _, contents)| { + let contents_str = String::from_utf8_lossy(contents); let file_path = entry.path().with_extension(""); let file_name = file_path.file_name().unwrap().to_string_lossy(); - let description = parse_lune_description_from_file(contents); + let description = parse_lune_description_from_file(&contents_str); (file_name.to_string(), description.unwrap_or_default()) }) .collect();