mirror of
https://github.com/tombi-toml/tombi.git
synced 2025-08-04 18:48:10 +00:00
refactor: tombi-wasm.
This commit is contained in:
parent
6482be30f4
commit
ae681536a9
15 changed files with 153 additions and 163 deletions
15
Cargo.lock
generated
15
Cargo.lock
generated
|
@ -2893,6 +2893,17 @@ dependencies = [
|
|||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde-wasm-bindgen"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
|
@ -3595,6 +3606,7 @@ dependencies = [
|
|||
"tower-lsp",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4002,6 +4014,7 @@ dependencies = [
|
|||
"serde",
|
||||
"tower-lsp",
|
||||
"tracing",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4062,6 +4075,7 @@ dependencies = [
|
|||
"log",
|
||||
"nu-ansi-term 0.50.1",
|
||||
"serde",
|
||||
"serde-wasm-bindgen",
|
||||
"serde_json",
|
||||
"serde_tombi",
|
||||
"tokio",
|
||||
|
@ -4071,6 +4085,7 @@ dependencies = [
|
|||
"tombi-future",
|
||||
"tombi-lexer",
|
||||
"tombi-schema-store",
|
||||
"tombi-text",
|
||||
"tombi-url",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
|
|
|
@ -12,6 +12,7 @@ thiserror.workspace = true
|
|||
tombi-text.workspace = true
|
||||
tower-lsp = { workspace = true, optional = true }
|
||||
tracing.workspace = true
|
||||
wasm-bindgen = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
clap.workspace = true
|
||||
|
@ -21,3 +22,4 @@ tracing-subscriber.workspace = true
|
|||
[features]
|
||||
default = ["lsp"]
|
||||
lsp = ["dep:tower-lsp"]
|
||||
wasm = ["dep:wasm-bindgen"]
|
||||
|
|
|
@ -6,6 +6,8 @@ pub use printer::Print;
|
|||
use tower_lsp::lsp_types::NumberOrString;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "wasm", wasm_bindgen::prelude::wasm_bindgen)]
|
||||
#[cfg_attr(feature = "wasm", derive(serde::Serialize))]
|
||||
pub struct Diagnostic {
|
||||
level: level::Level,
|
||||
code: String,
|
||||
|
|
|
@ -11,7 +11,6 @@ use countme::Count;
|
|||
use crate::{
|
||||
arc::{Arc, HeaderSlice, ThinArc},
|
||||
green::{GreenElement, GreenElementRef, SyntaxKind},
|
||||
utility_types::static_assert,
|
||||
GreenToken, NodeOrToken,
|
||||
};
|
||||
|
||||
|
@ -38,7 +37,7 @@ pub(crate) enum GreenChild {
|
|||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
static_assert!(mem::size_of::<GreenChild>() == mem::size_of::<usize>() * 3);
|
||||
crate::utility_types::static_assert!(mem::size_of::<GreenChild>() == mem::size_of::<usize>() * 3);
|
||||
|
||||
type Repr = HeaderSlice<GreenNodeHead, [GreenChild]>;
|
||||
type ReprThin = HeaderSlice<GreenNodeHead, [GreenChild; 0]>;
|
||||
|
|
|
@ -156,6 +156,7 @@ macro_rules! _static_assert {
|
|||
};
|
||||
}
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use _static_assert as static_assert;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
|
@ -9,6 +9,7 @@ license.workspace = true
|
|||
serde = { workspace = true, optional = true }
|
||||
tower-lsp = { workspace = true, optional = true }
|
||||
tracing.workspace = true
|
||||
wasm-bindgen = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions.workspace = true
|
||||
|
@ -18,3 +19,4 @@ rstest.workspace = true
|
|||
default = ["lsp", "serde"]
|
||||
lsp = ["dep:tower-lsp"]
|
||||
serde = ["dep:serde"]
|
||||
wasm = ["dep:wasm-bindgen", "serde"]
|
||||
|
|
|
@ -6,6 +6,8 @@ use std::{
|
|||
use crate::{Column, Line, RelativePosition};
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "wasm", wasm_bindgen::prelude::wasm_bindgen)]
|
||||
#[cfg_attr(feature = "wasm", derive(serde::Serialize))]
|
||||
pub struct Position {
|
||||
pub line: Line,
|
||||
pub column: Column,
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::ops::{Add, AddAssign};
|
|||
use crate::{Column, Line, Position, RelativePosition};
|
||||
|
||||
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[cfg_attr(feature = "wasm", wasm_bindgen::prelude::wasm_bindgen)]
|
||||
#[cfg_attr(feature = "wasm", derive(serde::Serialize))]
|
||||
pub struct Range {
|
||||
// Invariant: start <= end
|
||||
pub start: Position,
|
||||
|
|
|
@ -41,6 +41,17 @@ macro_rules! define_toml_version {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TomlVersion {
|
||||
pub fn from_str(value: &str) -> Option<Self> {
|
||||
match value {
|
||||
$(
|
||||
$version => Some(Self::$variant),
|
||||
)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -129,37 +129,38 @@ pub fn try_from_url(config_url: &url::Url) -> Result<Option<Config>, tombi_confi
|
|||
|
||||
/// Load the config from the current directory.
|
||||
pub fn load_with_path() -> Result<(Config, Option<std::path::PathBuf>), tombi_config::Error> {
|
||||
let mut current_dir = std::env::current_dir().unwrap();
|
||||
loop {
|
||||
let config_path = current_dir.join(TOMBI_TOML_FILENAME);
|
||||
if config_path.is_file() {
|
||||
tracing::debug!("\"{}\" found at {:?}", TOMBI_TOML_FILENAME, &config_path);
|
||||
if let Ok(mut current_dir) = std::env::current_dir() {
|
||||
loop {
|
||||
let config_path = current_dir.join(TOMBI_TOML_FILENAME);
|
||||
if config_path.is_file() {
|
||||
tracing::debug!("\"{}\" found at {:?}", TOMBI_TOML_FILENAME, &config_path);
|
||||
|
||||
let Some(config) = try_from_path(&config_path)? else {
|
||||
unreachable!("tombi.toml should always be parsed successfully.");
|
||||
};
|
||||
let Some(config) = try_from_path(&config_path)? else {
|
||||
unreachable!("tombi.toml should always be parsed successfully.");
|
||||
};
|
||||
|
||||
return Ok((config, Some(config_path)));
|
||||
}
|
||||
return Ok((config, Some(config_path)));
|
||||
}
|
||||
|
||||
let pyproject_toml_path = current_dir.join(PYPROJECT_TOML_FILENAME);
|
||||
if pyproject_toml_path.exists() {
|
||||
tracing::debug!(
|
||||
"\"{}\" found at {:?}",
|
||||
PYPROJECT_TOML_FILENAME,
|
||||
pyproject_toml_path
|
||||
);
|
||||
let pyproject_toml_path = current_dir.join(PYPROJECT_TOML_FILENAME);
|
||||
if pyproject_toml_path.exists() {
|
||||
tracing::debug!(
|
||||
"\"{}\" found at {:?}",
|
||||
PYPROJECT_TOML_FILENAME,
|
||||
pyproject_toml_path
|
||||
);
|
||||
|
||||
match try_from_path(&pyproject_toml_path)? {
|
||||
Some(config) => return Ok((config, Some(pyproject_toml_path))),
|
||||
None => {
|
||||
tracing::debug!("No [tool.tombi] found in {:?}", &pyproject_toml_path);
|
||||
}
|
||||
};
|
||||
}
|
||||
match try_from_path(&pyproject_toml_path)? {
|
||||
Some(config) => return Ok((config, Some(pyproject_toml_path))),
|
||||
None => {
|
||||
tracing::debug!("No [tool.tombi] found in {:?}", &pyproject_toml_path);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if !current_dir.pop() {
|
||||
break;
|
||||
if !current_dir.pop() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,14 +19,15 @@ nu-ansi-term.workspace = true
|
|||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde_tombi = { workspace = true, default-features = false, features = ["wasm"] }
|
||||
# serde_tombi = { workspace = true, default-features = false }
|
||||
serde-wasm-bindgen = "0.6.5"
|
||||
tokio = { workspace = true, features = ["macros"] }
|
||||
tombi-config.workspace = true
|
||||
tombi-diagnostic.workspace = true
|
||||
tombi-diagnostic = { workspace = true, features = ["wasm"] }
|
||||
tombi-formatter.workspace = true
|
||||
tombi-future = { workspace = true, features = ["wasm"] }
|
||||
tombi-lexer.workspace = true
|
||||
tombi-schema-store = { workspace = true, features = ["wasm"] }
|
||||
tombi-text = { workspace = true, features = ["wasm"] }
|
||||
tombi-url = { workspace = true, features = ["wasm"] }
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
use crate::pretty_buf::PrettyBuf;
|
||||
use itertools::Either::Right;
|
||||
use std::path::PathBuf;
|
||||
use tombi_config::{Config, TomlVersion};
|
||||
use tombi_diagnostic::Print;
|
||||
use tombi_formatter::formatter::definitions::FormatDefinitions;
|
||||
use tombi_formatter::Formatter;
|
||||
|
||||
pub async fn format(target_path: PathBuf, target_content: String) -> Result<String, String> {
|
||||
let toml_version = TomlVersion::V1_0_0;
|
||||
let schema_store =
|
||||
tombi_schema_store::SchemaStore::new_with_options(tombi_schema_store::Options {
|
||||
offline: Some(false),
|
||||
strict: None,
|
||||
});
|
||||
|
||||
schema_store
|
||||
.load_config(&Config::default(), None)
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let options = Default::default();
|
||||
let definitions = FormatDefinitions::default();
|
||||
let formatter = Formatter::new(
|
||||
toml_version,
|
||||
&definitions,
|
||||
&options,
|
||||
Some(Right(&target_path)),
|
||||
&schema_store,
|
||||
);
|
||||
|
||||
let mut printer = PrettyBuf::new();
|
||||
|
||||
match formatter.format(&target_content).await {
|
||||
Ok(formatted) => Ok(formatted),
|
||||
Err(diagnostics) => {
|
||||
diagnostics
|
||||
.into_iter()
|
||||
.map(|diagnostic| diagnostic.with_source_file(target_path.clone()))
|
||||
.collect::<Vec<_>>()
|
||||
.print(&mut printer);
|
||||
|
||||
Err(printer.get())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
mod format;
|
||||
mod pretty_buf;
|
||||
|
||||
use js_sys::Promise;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use serde_wasm_bindgen;
|
||||
use tombi_config::TomlVersion;
|
||||
use tombi_diagnostic::Diagnostic;
|
||||
use tombi_formatter::FormatDefinitions;
|
||||
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
|
||||
use wasm_bindgen_futures::future_to_promise;
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
|
@ -11,13 +12,83 @@ pub fn start() {
|
|||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn format(file_name: String, content: String) -> Promise {
|
||||
let fut = format::format(std::path::PathBuf::from(file_name), content);
|
||||
pub fn format(source: String, file_path: Option<String>, toml_version: Option<String>) -> Promise {
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
#[serde(untagged)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
enum FormatError {
|
||||
Error { error: String },
|
||||
Diagnostics { diagnostics: Vec<Diagnostic> },
|
||||
}
|
||||
|
||||
async fn inner_format(
|
||||
source: String,
|
||||
file_path: Option<String>,
|
||||
toml_version: Option<String>,
|
||||
) -> Result<String, FormatError> {
|
||||
let toml_version = match toml_version {
|
||||
Some(v) => match TomlVersion::from_str(&v) {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
return Err(FormatError::Error {
|
||||
error: "Invalid TOML version".to_string(),
|
||||
});
|
||||
}
|
||||
},
|
||||
None => TomlVersion::default(),
|
||||
};
|
||||
|
||||
let (config, config_path) = match serde_tombi::config::load_with_path() {
|
||||
Ok((config, config_path)) => (config, config_path),
|
||||
Err(err) => {
|
||||
return Err(FormatError::Error {
|
||||
error: err.to_string(),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let schema_options = config.schema.as_ref();
|
||||
let schema_store =
|
||||
tombi_schema_store::SchemaStore::new_with_options(tombi_schema_store::Options {
|
||||
offline: None,
|
||||
strict: schema_options.and_then(|schema_options| schema_options.strict()),
|
||||
});
|
||||
|
||||
tracing::info!("config_path: {:?}", config_path);
|
||||
tracing::info!("config: {:?}", config);
|
||||
if let Err(error) = schema_store
|
||||
.load_config(&config, config_path.as_deref())
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
{
|
||||
return Err(FormatError::Error { error });
|
||||
}
|
||||
|
||||
let format_options = config.format.clone().unwrap_or_default();
|
||||
let format_definitions = FormatDefinitions::default();
|
||||
|
||||
let file_path_buf = file_path.map(|path| std::path::PathBuf::from(path));
|
||||
match tombi_formatter::Formatter::new(
|
||||
toml_version,
|
||||
&format_definitions,
|
||||
&format_options,
|
||||
file_path_buf
|
||||
.as_deref()
|
||||
.map(|path| itertools::Either::Right(path)),
|
||||
&schema_store,
|
||||
)
|
||||
.format(&source)
|
||||
.await
|
||||
{
|
||||
Ok(t) => Ok(t),
|
||||
Err(diagnostics) => Err(FormatError::Diagnostics { diagnostics }),
|
||||
}
|
||||
}
|
||||
|
||||
future_to_promise(async move {
|
||||
match fut.await {
|
||||
match inner_format(source, file_path, toml_version).await {
|
||||
Ok(t) => Ok(JsValue::from_str(&t)),
|
||||
Err(e) => Err(JsValue::from_str(&e)),
|
||||
Err(e) => Err(serde_wasm_bindgen::to_value(&e).unwrap()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,74 +0,0 @@
|
|||
use nu_ansi_term::{Color, Style};
|
||||
use std::fmt;
|
||||
use std::fmt::Write;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tombi_diagnostic::printer::Simple;
|
||||
use tombi_diagnostic::{Diagnostic, Level, Print};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PrettyBuf(Arc<Mutex<String>>);
|
||||
|
||||
impl Default for PrettyBuf {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl PrettyBuf {
|
||||
pub fn new() -> Self {
|
||||
Self(Arc::new(Mutex::new(String::new())))
|
||||
}
|
||||
|
||||
pub fn get(&self) -> String {
|
||||
self.0.lock().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for PrettyBuf {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
let mut guard = self.0.lock().unwrap();
|
||||
guard.push_str(s);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Print<PrettyBuf> for Level {
|
||||
fn print(&self, _printer: &mut PrettyBuf) {
|
||||
self.print(&mut Simple);
|
||||
}
|
||||
}
|
||||
|
||||
impl Print<PrettyBuf> for Diagnostic {
|
||||
fn print(&self, printer: &mut PrettyBuf) {
|
||||
self.level().print(printer);
|
||||
writeln!(printer, ": {}", Style::new().bold().paint(self.message())).unwrap();
|
||||
|
||||
let at_style: Style = Style::new().fg(Color::DarkGray);
|
||||
let link_style: Style = Style::new().fg(Color::Cyan);
|
||||
if let Some(source_file) = self.source_file() {
|
||||
writeln!(
|
||||
printer,
|
||||
" {} {}",
|
||||
at_style.paint("at"),
|
||||
link_style.paint(format!(
|
||||
"{}:{}:{}",
|
||||
source_file.display(),
|
||||
self.position().line + 1,
|
||||
self.position().column + 1
|
||||
)),
|
||||
)
|
||||
.unwrap();
|
||||
} else {
|
||||
writeln!(
|
||||
printer,
|
||||
" {}",
|
||||
at_style.paint(format!(
|
||||
"at line {} column {}",
|
||||
self.position().line + 1,
|
||||
self.position().column + 1
|
||||
)),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,15 +35,16 @@
|
|||
|
||||
init().then(() => {
|
||||
document.getElementById("run").onclick = async () => {
|
||||
const file = document.getElementById("file-name").value;
|
||||
const filename = document.getElementById("file-name").value;
|
||||
const text = document.getElementById("content").value;
|
||||
const output = document.getElementById("output");
|
||||
|
||||
try {
|
||||
const result = await format(file, text);
|
||||
output.textContent = result;
|
||||
} catch (e) {
|
||||
output.textContent = "Error: " + e;
|
||||
const result = await format(text, filename);
|
||||
|
||||
output.textContent = result.toString();
|
||||
} catch (error) {
|
||||
output.textContent = JSON.stringify(error, null, 2);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue