Skip to content
Draft
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
504 changes: 443 additions & 61 deletions Cargo.lock

Large diffs are not rendered by default.

25 changes: 24 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ resolver = "2"
[workspace.package]
version = "0.1.7"
edition = "2024"
rust-version = "1.88" # MSRV
rust-version = "1.88" # MSRV
authors = ["DaniPopes <[email protected]>"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/paradigmxyz/solar"
Expand Down Expand Up @@ -92,6 +92,7 @@ solar-cli = { version = "=0.1.7", path = "crates/cli", default-features = false
solar-config = { version = "=0.1.7", path = "crates/config", default-features = false }
solar-data-structures = { version = "=0.1.7", path = "crates/data-structures", default-features = false }
solar-interface = { version = "=0.1.7", path = "crates/interface", default-features = false }
solar-lsp = { version = "=0.1.7", path = "crates/lsp", default-features = false }
solar-macros = { version = "=0.1.7", path = "crates/macros", default-features = false }
solar-parse = { version = "=0.1.7", path = "crates/parse", default-features = false }
solar-sema = { version = "=0.1.7", path = "crates/sema", default-features = false }
Expand Down Expand Up @@ -151,6 +152,18 @@ snapbox = "0.6"
tempfile = "3.9"
tester = "0.9"

# lsp
async-lsp = { version = "0.2", default-features = false, features = [
"client-monitor",
"stdio",
"tokio",
"tracing",
] }
crop = "0.4"
# See <https://github.com/gluon-lang/lsp-types/issues/284>
lsp-types = "0.95.0"
tower = "0.5"

# misc
arrayvec = "0.7"
bitflags = "2.4"
Expand All @@ -168,6 +181,7 @@ inturn = "0.1.0"
libc = "0.2"
md-5 = "0.10"
memchr = "2.7"
normalize-path = "0.2.1"
once_map = { version = "0.4.20", default-features = false, features = ["std"] }
paste = "1.0"
petgraph = "0.8"
Expand All @@ -177,8 +191,17 @@ scoped-tls = "1.0"
semver = "1.0"
smallvec = { version = "1", features = ["const_generics", "union"] }
thread_local = "1.1"
tokio = { version = "1.47", default-features = false }
tokio-util = { version = "0.7", default-features = false }
typed-arena = "2.0"
unicode-width = "0.2"
vergen = "8"

## Pinned dependencies. Enabled for the workspace in crates/solar.

# Use unicode-rs which has a smaller binary size than the default ICU4X as the IDNA backend, used
# by the `url` crate.
# See the `idna_adapter` README.md for more details: https://docs.rs/crate/idna_adapter/latest
idna_adapter = "=1.1.0"

[patch.crates-io]
24 changes: 24 additions & 0 deletions crates/cli/src/args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use clap::{Parser, Subcommand};
use solar_config::Opts;

/// Blazingly fast Solidity compiler.
#[derive(Clone, Debug, Default, Parser)]
#[command(
name = "solar",
version = crate::version::SHORT_VERSION,
long_version = crate::version::LONG_VERSION,
arg_required_else_help = true,
)]
#[allow(clippy::manual_non_exhaustive)]
pub struct Args {
#[command(subcommand)]
pub commands: Option<Subcommands>,
#[command(flatten)]
pub default_compile: Opts,
}

#[derive(Debug, Clone, Subcommand)]
pub enum Subcommands {
/// Start the language server.
Lsp,
}
10 changes: 7 additions & 3 deletions crates/cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ pub mod utils;
#[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))]
pub mod signal_handler;

mod args;
pub use args::{Args, Subcommands};

/// Signal handler to extract a backtrace from stack overflow.
///
/// This is a no-op because this platform doesn't support our signal handler's requirements.
Expand All @@ -34,13 +37,14 @@ use alloy_primitives as _;

use tracing as _;

pub fn parse_args<I, T>(itr: I) -> Result<Opts, clap::Error>
pub fn parse_args<I, T>(itr: I) -> Result<Args, clap::Error>
where
I: IntoIterator<Item = T>,
T: Into<std::ffi::OsString> + Clone,
{
let mut opts = Opts::try_parse_from(itr)?;
opts.finish()?;
let mut opts = Args::try_parse_from(itr)?;
opts.default_compile.finish()?;

Ok(opts)
}

Expand Down
7 changes: 0 additions & 7 deletions crates/config/src/opts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,8 @@ use clap::{Parser, ValueHint};

// TODO: implement `allow_paths`.

/// Blazingly fast Solidity compiler.
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "clap", derive(Parser))]
#[cfg_attr(feature = "clap", command(
name = "solar",
version = crate::version::SHORT_VERSION,
long_version = crate::version::LONG_VERSION,
arg_required_else_help = true,
))]
#[allow(clippy::manual_non_exhaustive)]
pub struct Opts {
/// Files to compile, or import remappings.
Expand Down
2 changes: 1 addition & 1 deletion crates/interface/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ dunce = "1"
inturn.workspace = true
itertools.workspace = true
itoa.workspace = true
normalize-path = "0.2.1"
normalize-path.workspace = true
once_map.workspace = true
rayon.workspace = true
scoped-tls.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion crates/interface/src/diagnostics/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub struct SpanLabel {
/// A collection of `Span`s.
///
/// Spans have two orthogonal attributes:
/// - They can be *primary spans*. In this case they are the locus of the error, and would be
/// - They can be *primary spans*. In this case they are the focus of the error, and would be
/// rendered with `^^^`.
/// - They can have a *label*. In this case, the label is written next to the mark in the snippet
/// when we render.
Expand Down
2 changes: 1 addition & 1 deletion crates/interface/src/source_map/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ pub struct SourceFile {
pub source_len: RelativeBytePos,
/// Locations of lines beginnings in the source code.
#[debug(skip)]
pub lines: Vec<RelativeBytePos>,
lines: Vec<RelativeBytePos>,
/// Locations of multi-byte characters in the source code.
#[debug(skip)]
pub multibyte_chars: Vec<MultiByteChar>,
Expand Down
48 changes: 48 additions & 0 deletions crates/lsp/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[package]
name = "solar-lsp"
description = "Solidity LSP"
homepage = "https://github.com/paradigmxyz/solar/tree/main/crates/lsp"

version.workspace = true
authors.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
keywords.workspace = true
categories.workspace = true

[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"-Zunstable-options",
"--generate-link-to-definition",
"--show-type-layout",
]

[lints]
workspace = true

[dependencies]
solar-config = { workspace = true, features = ["version"] }
solar-interface.workspace = true
solar-sema.workspace = true

async-lsp = { workspace = true, features = ["omni-trait"] }
crop = { workspace = true, features = ["utf16-metric"] }
lsp-types.workspace = true
normalize-path.workspace = true
serde.workspace = true
serde_json.workspace = true
tower.workspace = true
tokio = { workspace = true, features = ["rt"] }
tracing.workspace = true

# This is needed because Windows does not support truly asynchronous pipes.
[target.'cfg(not(unix))'.dependencies]
tokio-util = { workspace = true, features = ["compat"] }
tokio = { workspace = true, features = ["rt", "io-std"] }

[features]
nightly = ["solar-config/nightly"]
version = ["solar-config/version"]
3 changes: 3 additions & 0 deletions crates/lsp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# solar-lsp

Solar LSP definitions and implementation.
92 changes: 92 additions & 0 deletions crates/lsp/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use std::{
env,
path::{Path, PathBuf},
};

use lsp_types::{
InitializeParams, ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind,
TextDocumentSyncOptions,
};
use tracing::{error, info};

use crate::workspace::manifest::ProjectManifest;

/// The LSP config.
///
/// This struct is internal only and should not be serialized or deserialized. Instead, values in
/// this struct are the full view of all merged config sources, such as `initialization_opts`,
/// on-disk config files (e.g. `foundry.toml`).
#[derive(Default, Clone, Debug)]
pub(crate) struct Config {
root_path: PathBuf,
workspace_roots: Vec<PathBuf>,
discovered_projects: Vec<ProjectManifest>,
}

impl Config {
pub(crate) fn new(root_path: PathBuf, workspace_roots: Vec<PathBuf>) -> Self {
Config { root_path, workspace_roots, discovered_projects: Default::default() }
}

pub(crate) fn rediscover_workspaces(&mut self) {
let discovered = ProjectManifest::discover_all(&self.workspace_roots);
info!("discovered projects: {:?}", discovered);
if discovered.is_empty() {
error!("failed to find any projects in {:?}", &self.workspace_roots);
}
self.discovered_projects = discovered;
}

pub(crate) fn remove_workspace(&mut self, path: &PathBuf) {
if let Some(pos) = self.workspace_roots.iter().position(|it| it == path) {
self.workspace_roots.remove(pos);
}
}

pub(crate) fn add_workspaces(&mut self, paths: impl Iterator<Item = PathBuf>) {
self.workspace_roots.extend(paths);
}

pub(crate) fn root_path(&self) -> &Path {
self.root_path.as_path()
}
}

pub(crate) fn negotiate_capabilities(params: InitializeParams) -> (ServerCapabilities, Config) {
// todo: make this absolute guaranteed
#[allow(deprecated)]
let root_path = match params.root_uri.and_then(|it| it.to_file_path().ok()) {
Some(it) => it,
None => {
// todo: unwrap
env::current_dir().unwrap()
}
};

// todo: make this absolute guaranteed
// The latest LSP spec mandates clients report `workspace_folders`, but some might still report
// `root_uri`.
let workspace_roots = params
.workspace_folders
.map(|workspaces| {
workspaces.into_iter().filter_map(|it| it.uri.to_file_path().ok()).collect::<Vec<_>>()
})
.filter(|workspaces| !workspaces.is_empty())
.unwrap_or_else(|| vec![root_path.clone()]);

(
ServerCapabilities {
text_document_sync: Some(TextDocumentSyncCapability::Options(
TextDocumentSyncOptions {
open_close: Some(true),
change: Some(TextDocumentSyncKind::INCREMENTAL),
will_save: None,
will_save_wait_until: None,
..Default::default()
},
)),
..Default::default()
},
Config::new(root_path, workspace_roots),
)
}
Loading
Loading