Try to automatically add typedefs to vscode settings or print them out

This commit is contained in:
Filip Tibell 2023-05-14 21:20:52 +02:00
parent 85bbcaabaa
commit f7d82f08b0
No known key found for this signature in database
4 changed files with 87 additions and 15 deletions

View file

@ -2,8 +2,6 @@
// Luau - disable Roblox features, enable Lune typedefs & requires // Luau - disable Roblox features, enable Lune typedefs & requires
"luau-lsp.sourcemap.enabled": false, "luau-lsp.sourcemap.enabled": false,
"luau-lsp.types.roblox": false, "luau-lsp.types.roblox": false,
"luau-lsp.types.definitionFiles": ["luneTypes.d.luau"],
"luau-lsp.types.documentationFiles": ["luneDocs.json"],
"luau-lsp.require.mode": "relativeToFile", "luau-lsp.require.mode": "relativeToFile",
// Luau - ignore type defs file in docs dir and dev scripts we use // Luau - ignore type defs file in docs dir and dev scripts we use
"luau-lsp.ignoreGlobs": [ "luau-lsp.ignoreGlobs": [

View file

@ -1,12 +1,18 @@
use std::process::ExitCode; use std::{
borrow::BorrowMut,
collections::HashMap,
path::{Path, PathBuf},
process::ExitCode,
};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use clap::{CommandFactory, Parser}; use clap::{CommandFactory, Parser};
use serde_json::Value as JsonValue;
use include_dir::{include_dir, Dir}; use include_dir::{include_dir, Dir};
use lune::Lune; use lune::Lune;
use tokio::{ use tokio::{
fs::read as read_to_vec, fs::{self, read as read_to_vec},
io::{stdin, AsyncReadExt}, io::{stdin, AsyncReadExt},
}; };
@ -81,6 +87,7 @@ impl Cli {
self self
} }
#[allow(clippy::too_many_lines)]
pub async fn run(self) -> Result<ExitCode> { pub async fn run(self) -> Result<ExitCode> {
// List files in `lune` and `.lune` directories, if wanted // List files in `lune` and `.lune` directories, if wanted
// This will also exit early and not run anything else // This will also exit early and not run anything else
@ -124,7 +131,43 @@ impl Cli {
return Ok(ExitCode::FAILURE); return Ok(ExitCode::FAILURE);
} }
if self.setup { if self.setup {
generate_typedef_files_from_definitions(&TYPEDEFS_DIR).await?; let generated_paths =
generate_typedef_files_from_definitions(&TYPEDEFS_DIR).await?;
let settings_json_path = PathBuf::from(".vscode/settings.json");
let message = match fs::metadata(&settings_json_path).await {
Ok(meta) if meta.is_file() => {
if try_add_generated_typedefs_vscode(&settings_json_path, &generated_paths).await.is_err() {
"These files can be added to your LSP settings for autocomplete and documentation."
} else {
"These files have now been added to your workspace LSP settings for Visual Studio Code."
}
}
_ => "These files can be added to your LSP settings for autocomplete and documentation.",
};
// HACK: We should probably just be serializing this hashmap to print it out, but
// that does not guarantee sorting and the sorted version is much easier to read
let mut sorted_names = generated_paths
.keys()
.map(ToString::to_string)
.collect::<Vec<_>>();
sorted_names.sort_unstable();
println!(
"Typedefs have been generated in the following locations:\n{{\n{}\n}}\n{message}",
sorted_names
.iter()
.map(|name| {
let path = generated_paths.get(name).unwrap();
format!(
" \"@lune/{}\": \"{}\",",
name,
path.canonicalize().unwrap().display()
)
})
.collect::<Vec<_>>()
.join("\n")
.strip_suffix(',')
.unwrap()
);
} }
} }
if self.script_path.is_none() { if self.script_path.is_none() {
@ -172,3 +215,29 @@ impl Cli {
}) })
} }
} }
async fn try_add_generated_typedefs_vscode(
settings_json_path: &Path,
generated_paths: &HashMap<String, PathBuf>,
) -> Result<()> {
// FUTURE: Use a jsonc or json5 to read this file instead since it may contain comments and fail
let settings_json_contents = fs::read(settings_json_path).await?;
let mut settings_changed: bool = false;
let mut settings_json: JsonValue = serde_json::from_slice(&settings_json_contents)?;
if let JsonValue::Object(settings) = settings_json.borrow_mut() {
if let Some(JsonValue::Object(aliases)) = settings.get_mut("luau-lsp.require.fileAliases") {
for (name, path) in generated_paths {
settings_changed = true;
aliases.insert(
format!("@lune/{name}"),
JsonValue::String(path.canonicalize().unwrap().to_string_lossy().to_string()),
);
}
}
}
if settings_changed {
let settings_json_new = serde_json::to_vec_pretty(&settings_json)?;
fs::write(settings_json_path, settings_json_new).await?;
}
Ok(())
}

View file

@ -1,4 +1,4 @@
use std::collections::HashMap; use std::{collections::HashMap, path::PathBuf};
use anyhow::Result; use anyhow::Result;
use include_dir::Dir; use include_dir::Dir;
@ -15,7 +15,9 @@ pub async fn generate_gitbook_dir_from_definitions(dir: &Dir<'_>) -> Result<()>
gitbook_dir::generate_from_type_definitions(definitions).await gitbook_dir::generate_from_type_definitions(definitions).await
} }
pub async fn generate_typedef_files_from_definitions(dir: &Dir<'_>) -> Result<()> { pub async fn generate_typedef_files_from_definitions(
dir: &Dir<'_>,
) -> Result<HashMap<String, PathBuf>> {
let definitions = read_typedefs_dir(dir)?; let definitions = read_typedefs_dir(dir)?;
typedef_files::generate_from_type_definitions(definitions).await typedef_files::generate_from_type_definitions(definitions).await
} }

View file

@ -1,4 +1,4 @@
use std::{collections::HashMap, fmt::Write}; use std::{collections::HashMap, fmt::Write, path::PathBuf};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use directories::UserDirs; use directories::UserDirs;
@ -13,7 +13,7 @@ const GENERATED_COMMENT_TAG: &str = "--!strict";
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub async fn generate_from_type_definitions( pub async fn generate_from_type_definitions(
api_reference: HashMap<String, DefinitionsTree>, api_reference: HashMap<String, DefinitionsTree>,
) -> Result<()> { ) -> Result<HashMap<String, PathBuf>> {
let mut dirs_to_write = Vec::new(); let mut dirs_to_write = Vec::new();
let mut files_to_write = Vec::new(); let mut files_to_write = Vec::new();
// Create the typedefs dir in the users cache dir // Create the typedefs dir in the users cache dir
@ -21,7 +21,7 @@ pub async fn generate_from_type_definitions(
.context("Failed to find user home directory")? .context("Failed to find user home directory")?
.home_dir() .home_dir()
.join(".lune") .join(".lune")
.join("typedefs") .join(".typedefs")
.join(env!("CARGO_PKG_VERSION")); .join(env!("CARGO_PKG_VERSION"));
dirs_to_write.push(cache_dir.clone()); dirs_to_write.push(cache_dir.clone());
// Make typedef files // Make typedef files
@ -36,8 +36,8 @@ pub async fn generate_from_type_definitions(
category_name.to_lowercase(), category_name.to_lowercase(),
env!("CARGO_PKG_VERSION") env!("CARGO_PKG_VERSION")
)?; )?;
write_tree(&mut contents, category_name, category_tree)?; write_tree(&mut contents, category_name.to_string(), category_tree)?;
files_to_write.push((path, contents)); files_to_write.push((category_name.to_lowercase(), path, contents));
} }
// Write all dirs and files only when we know generation was successful // Write all dirs and files only when we know generation was successful
let futs_dirs = dirs_to_write let futs_dirs = dirs_to_write
@ -45,12 +45,15 @@ pub async fn generate_from_type_definitions(
.map(create_dir_all) .map(create_dir_all)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let futs_files = files_to_write let futs_files = files_to_write
.drain(..) .iter()
.map(|(path, contents)| write(path, contents)) .map(|(_, path, contents)| write(path, contents))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
try_join_all(futs_dirs).await?; try_join_all(futs_dirs).await?;
try_join_all(futs_files).await?; try_join_all(futs_files).await?;
Ok(()) Ok(files_to_write
.drain(..)
.map(|(name, path, _)| (name, path))
.collect::<HashMap<_, _>>())
} }
fn make_return_table_item(item: &DefinitionsItem) -> Result<String> { fn make_return_table_item(item: &DefinitionsItem) -> Result<String> {