Start stdx

This crate will hold everything to small to be worth publishing
This commit is contained in:
Aleksey Kladov 2020-03-28 11:01:25 +01:00
parent a1fea0d34e
commit b764c38436
13 changed files with 163 additions and 104 deletions

22
Cargo.lock generated
View File

@ -353,12 +353,6 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
[[package]]
name = "format-buf"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7aea5a5909a74969507051a3b17adc84737e31a5f910559892aedce026f4d53"
[[package]] [[package]]
name = "fs_extra" name = "fs_extra"
version = "1.1.0" version = "1.1.0"
@ -573,12 +567,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f52a11f73b88fab829a0e4d9e13ea5982c7ac457c72eb3541d82a4afdfce4ff" checksum = "2f52a11f73b88fab829a0e4d9e13ea5982c7ac457c72eb3541d82a4afdfce4ff"
[[package]]
name = "join_to_string"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dc7a5290e8c2606ce2be49f456d50f69173cb96d1541e4f66e34ac8b331a98f"
[[package]] [[package]]
name = "kernel32-sys" name = "kernel32-sys"
version = "0.2.2" version = "0.2.2"
@ -885,9 +873,7 @@ name = "ra_assists"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"either", "either",
"format-buf",
"itertools 0.9.0", "itertools 0.9.0",
"join_to_string",
"ra_db", "ra_db",
"ra_fmt", "ra_fmt",
"ra_hir", "ra_hir",
@ -896,6 +882,7 @@ dependencies = [
"ra_syntax", "ra_syntax",
"ra_text_edit", "ra_text_edit",
"rustc-hash", "rustc-hash",
"stdx",
"test_utils", "test_utils",
] ]
@ -1023,11 +1010,9 @@ name = "ra_ide"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"either", "either",
"format-buf",
"indexmap", "indexmap",
"insta", "insta",
"itertools 0.9.0", "itertools 0.9.0",
"join_to_string",
"log", "log",
"ra_assists", "ra_assists",
"ra_cfg", "ra_cfg",
@ -1040,6 +1025,7 @@ dependencies = [
"ra_text_edit", "ra_text_edit",
"rand", "rand",
"rustc-hash", "rustc-hash",
"stdx",
"test_utils", "test_utils",
] ]
@ -1488,6 +1474,10 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "stdx"
version = "0.1.0"
[[package]] [[package]]
name = "superslice" name = "superslice"
version = "1.0.0" version = "1.0.0"

View File

@ -8,12 +8,12 @@ authors = ["rust-analyzer developers"]
doctest = false doctest = false
[dependencies] [dependencies]
format-buf = "1.0.0"
join_to_string = "0.1.3"
rustc-hash = "1.1.0" rustc-hash = "1.1.0"
itertools = "0.9.0" itertools = "0.9.0"
either = "1.5.3" either = "1.5.3"
stdx = { path = "../stdx" }
ra_syntax = { path = "../ra_syntax" } ra_syntax = { path = "../ra_syntax" }
ra_text_edit = { path = "../ra_text_edit" } ra_text_edit = { path = "../ra_text_edit" }
ra_fmt = { path = "../ra_fmt" } ra_fmt = { path = "../ra_fmt" }

View File

@ -1,17 +1,13 @@
//! FIXME: write short doc here
use join_to_string::join;
use ra_syntax::{ use ra_syntax::{
ast::{self, AstNode}, ast::{self, AstNode},
Direction, SmolStr, Direction, SmolStr,
SyntaxKind::{IDENT, WHITESPACE}, SyntaxKind::{IDENT, WHITESPACE},
TextRange, TextUnit, TextRange, TextUnit,
}; };
use stdx::SepBy;
use crate::{Assist, AssistCtx, AssistId}; use crate::{Assist, AssistCtx, AssistId};
const DERIVE_TRAIT: &str = "derive";
// Assist: add_custom_impl // Assist: add_custom_impl
// //
// Adds impl block for derived trait. // Adds impl block for derived trait.
@ -38,7 +34,7 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
.descendants_with_tokens() .descendants_with_tokens()
.filter(|t| t.kind() == IDENT) .filter(|t| t.kind() == IDENT)
.find_map(|i| i.into_token()) .find_map(|i| i.into_token())
.filter(|t| *t.text() == DERIVE_TRAIT)? .filter(|t| *t.text() == "derive")?
.text() .text()
.clone(); .clone();
@ -63,8 +59,7 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
.filter(|t| t != trait_token.text()) .filter(|t| t != trait_token.text())
.collect::<Vec<SmolStr>>(); .collect::<Vec<SmolStr>>();
let has_more_derives = !new_attr_input.is_empty(); let has_more_derives = !new_attr_input.is_empty();
let new_attr_input = let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string();
join(new_attr_input.iter()).separator(", ").surround_with("(", ")").to_string();
let new_attr_input_len = new_attr_input.len(); let new_attr_input_len = new_attr_input.len();
let mut buf = String::new(); let mut buf = String::new();
@ -100,9 +95,10 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
use crate::helpers::{check_assist, check_assist_not_applicable}; use crate::helpers::{check_assist, check_assist_not_applicable};
use super::*;
#[test] #[test]
fn add_custom_impl_for_unique_input() { fn add_custom_impl_for_unique_input() {
check_assist( check_assist(

View File

@ -1,9 +1,8 @@
use format_buf::format;
use join_to_string::join;
use ra_syntax::{ use ra_syntax::{
ast::{self, AstNode, NameOwner, TypeParamsOwner}, ast::{self, AstNode, NameOwner, TypeParamsOwner},
TextUnit, TextUnit,
}; };
use stdx::{format_to, SepBy};
use crate::{Assist, AssistCtx, AssistId}; use crate::{Assist, AssistCtx, AssistId};
@ -36,7 +35,7 @@ pub(crate) fn add_impl(ctx: AssistCtx) -> Option<Assist> {
let mut buf = String::new(); let mut buf = String::new();
buf.push_str("\n\nimpl"); buf.push_str("\n\nimpl");
if let Some(type_params) = &type_params { if let Some(type_params) = &type_params {
format!(buf, "{}", type_params.syntax()); format_to!(buf, "{}", type_params.syntax());
} }
buf.push_str(" "); buf.push_str(" ");
buf.push_str(name.text().as_str()); buf.push_str(name.text().as_str());
@ -47,7 +46,9 @@ pub(crate) fn add_impl(ctx: AssistCtx) -> Option<Assist> {
.map(|it| it.text().clone()); .map(|it| it.text().clone());
let type_params = let type_params =
type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
join(lifetime_params.chain(type_params)).surround_with("<", ">").to_buf(&mut buf);
let generic_params = lifetime_params.chain(type_params).sep_by(", ");
format_to!(buf, "<{}>", generic_params)
} }
buf.push_str(" {\n"); buf.push_str(" {\n");
edit.set_cursor(start_offset + TextUnit::of_str(&buf)); edit.set_cursor(start_offset + TextUnit::of_str(&buf));

View File

@ -1,14 +1,11 @@
use std::fmt::Write;
use format_buf::format;
use hir::Adt; use hir::Adt;
use join_to_string::join;
use ra_syntax::{ use ra_syntax::{
ast::{ ast::{
self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner, self, AstNode, NameOwner, StructKind, TypeAscriptionOwner, TypeParamsOwner, VisibilityOwner,
}, },
TextUnit, T, TextUnit, T,
}; };
use stdx::{format_to, SepBy};
use crate::{Assist, AssistCtx, AssistId}; use crate::{Assist, AssistCtx, AssistId};
@ -53,24 +50,22 @@ pub(crate) fn add_new(ctx: AssistCtx) -> Option<Assist> {
buf.push('\n'); buf.push('\n');
} }
let vis = strukt.visibility().map(|v| format!("{} ", v.syntax())); let vis = strukt.visibility().map(|v| format!("{} ", v));
let vis = vis.as_deref().unwrap_or(""); let vis = vis.as_deref().unwrap_or("");
write!(&mut buf, " {}fn new(", vis).unwrap();
join(field_list.fields().filter_map(|f| { let params = field_list
Some(format!("{}: {}", f.name()?.syntax().text(), f.ascribed_type()?.syntax().text())) .fields()
})) .filter_map(|f| {
.separator(", ") Some(format!(
.to_buf(&mut buf); "{}: {}",
f.name()?.syntax().text(),
f.ascribed_type()?.syntax().text()
))
})
.sep_by(", ");
let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");
buf.push_str(") -> Self { Self {"); format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
join(field_list.fields().filter_map(|f| Some(f.name()?.syntax().text())))
.separator(", ")
.surround_with(" ", " ")
.to_buf(&mut buf);
buf.push_str("} }");
let (start_offset, end_offset) = impl_def let (start_offset, end_offset) = impl_def
.and_then(|impl_def| { .and_then(|impl_def| {
@ -103,7 +98,7 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String {
let mut buf = String::with_capacity(code.len()); let mut buf = String::with_capacity(code.len());
buf.push_str("\n\nimpl"); buf.push_str("\n\nimpl");
if let Some(type_params) = &type_params { if let Some(type_params) = &type_params {
format!(buf, "{}", type_params.syntax()); format_to!(buf, "{}", type_params.syntax());
} }
buf.push_str(" "); buf.push_str(" ");
buf.push_str(strukt.name().unwrap().text().as_str()); buf.push_str(strukt.name().unwrap().text().as_str());
@ -114,10 +109,10 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String {
.map(|it| it.text().clone()); .map(|it| it.text().clone());
let type_params = let type_params =
type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
join(lifetime_params.chain(type_params)).surround_with("<", ">").to_buf(&mut buf); format_to!(buf, "<{}>", lifetime_params.chain(type_params).sep_by(", "))
} }
format!(&mut buf, " {{\n{}\n}}\n", code); format_to!(buf, " {{\n{}\n}}\n", code);
buf buf
} }

View File

@ -1,4 +1,3 @@
use format_buf::format;
use ra_syntax::{ use ra_syntax::{
ast::{self, AstNode}, ast::{self, AstNode},
SyntaxKind::{ SyntaxKind::{
@ -7,6 +6,7 @@ use ra_syntax::{
}, },
SyntaxNode, TextUnit, SyntaxNode, TextUnit,
}; };
use stdx::format_to;
use test_utils::tested_by; use test_utils::tested_by;
use crate::{Assist, AssistCtx, AssistId}; use crate::{Assist, AssistCtx, AssistId};
@ -52,7 +52,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option<Assist> {
buf.push_str("let var_name = "); buf.push_str("let var_name = ");
TextUnit::of_str("let ") TextUnit::of_str("let ")
}; };
format!(buf, "{}", expr.syntax()); format_to!(buf, "{}", expr.syntax());
let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
let is_full_stmt = if let Some(expr_stmt) = &full_stmt { let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())

View File

@ -12,14 +12,14 @@ wasm = []
[dependencies] [dependencies]
either = "1.5.3" either = "1.5.3"
format-buf = "1.0.0"
indexmap = "1.3.2" indexmap = "1.3.2"
itertools = "0.9.0" itertools = "0.9.0"
join_to_string = "0.1.3"
log = "0.4.8" log = "0.4.8"
rustc-hash = "1.1.0" rustc-hash = "1.1.0"
rand = { version = "0.7.3", features = ["small_rng"] } rand = { version = "0.7.3", features = ["small_rng"] }
stdx = { path = "../stdx" }
ra_syntax = { path = "../ra_syntax" } ra_syntax = { path = "../ra_syntax" }
ra_text_edit = { path = "../ra_text_edit" } ra_text_edit = { path = "../ra_text_edit" }
ra_db = { path = "../ra_db" } ra_db = { path = "../ra_db" }

View File

@ -1,15 +1,14 @@
//! This modules takes care of rendering various definitions as completion items. //! This modules takes care of rendering various definitions as completion items.
use hir::{Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type}; use hir::{Docs, HasAttrs, HasSource, HirDisplay, ScopeDef, StructKind, Type};
use join_to_string::join;
use ra_syntax::ast::NameOwner; use ra_syntax::ast::NameOwner;
use stdx::SepBy;
use test_utils::tested_by; use test_utils::tested_by;
use crate::completion::{
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
};
use crate::{ use crate::{
completion::{
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
},
display::{const_label, macro_label, type_label, FunctionSignature}, display::{const_label, macro_label, type_label, FunctionSignature},
RootDatabase, RootDatabase,
}; };
@ -221,13 +220,13 @@ impl Completions {
builder = builder.trigger_call_info(); builder = builder.trigger_call_info();
let snippet = if ctx.options.add_call_argument_snippets { let snippet = if ctx.options.add_call_argument_snippets {
let to_skip = if has_self_param { 1 } else { 0 }; let to_skip = if has_self_param { 1 } else { 0 };
let function_params_snippet = join( let function_params_snippet = function_signature
function_signature.parameter_names.iter().skip(to_skip).enumerate().map( .parameter_names
|(index, param_name)| format!("${{{}:{}}}", index + 1, param_name), .iter()
), .skip(to_skip)
) .enumerate()
.separator(", ") .map(|(index, param_name)| format!("${{{}:{}}}", index + 1, param_name))
.to_string(); .sep_by(", ");
format!("{}({})$0", name, function_params_snippet) format!("{}({})$0", name, function_params_snippet)
} else { } else {
format!("{}($0)", name) format!("{}($0)", name)
@ -281,18 +280,16 @@ impl Completions {
.into_iter() .into_iter()
.map(|field| (field.name(ctx.db), field.signature_ty(ctx.db))); .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db)));
let detail = match variant.kind(ctx.db) { let detail = match variant.kind(ctx.db) {
StructKind::Tuple | StructKind::Unit => { StructKind::Tuple | StructKind::Unit => detail_types
join(detail_types.map(|(_, t)| t.display(ctx.db).to_string())) .map(|(_, t)| t.display(ctx.db).to_string())
.separator(", ") .sep_by(", ")
.surround_with("(", ")") .surround_with("(", ")")
.to_string() .to_string(),
} StructKind::Record => detail_types
StructKind::Record => { .map(|(n, t)| format!("{}: {}", n, t.display(ctx.db).to_string()))
join(detail_types.map(|(n, t)| format!("{}: {}", n, t.display(ctx.db).to_string()))) .sep_by(", ")
.separator(", ") .surround_with("{ ", " }")
.surround_with("{ ", " }") .to_string(),
.to_string()
}
}; };
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
.kind(CompletionItemKind::EnumVariant) .kind(CompletionItemKind::EnumVariant)

View File

@ -200,8 +200,8 @@ fn check_struct_shorthand_initialization(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use insta::assert_debug_snapshot; use insta::assert_debug_snapshot;
use join_to_string::join;
use ra_syntax::SourceFile; use ra_syntax::SourceFile;
use stdx::SepBy;
use test_utils::assert_eq_text; use test_utils::assert_eq_text;
use crate::mock_analysis::{analysis_and_position, single_file}; use crate::mock_analysis::{analysis_and_position, single_file};
@ -254,16 +254,12 @@ mod tests {
.map(|it| it.len() - it.trim_start().len()) .map(|it| it.len() - it.trim_start().len())
.next() .next()
.expect("empty fixture"); .expect("empty fixture");
let after = join(after.lines().filter_map(|line| { let after = after
if line.len() > margin { .lines()
Some(&line[margin..]) .filter_map(|line| if line.len() > margin { Some(&line[margin..]) } else { None })
} else { .sep_by("\n")
None .suffix("\n")
} .to_string();
}))
.separator("\n")
.suffix("\n")
.to_string();
assert_eq_text!(&after, &actual); assert_eq_text!(&after, &actual);
assert!( assert!(

View File

@ -1,12 +1,14 @@
//! FIXME: write short doc here //! FIXME: write short doc here
use std::fmt::{self, Display}; use std::{
convert::From,
fmt::{self, Display},
};
use hir::{Docs, Documentation, HasSource, HirDisplay}; use hir::{Docs, Documentation, HasSource, HirDisplay};
use join_to_string::join;
use ra_ide_db::RootDatabase; use ra_ide_db::RootDatabase;
use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner}; use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
use std::convert::From; use stdx::SepBy;
use crate::display::{generic_parameters, where_predicates}; use crate::display::{generic_parameters, where_predicates};
@ -227,21 +229,17 @@ impl Display for FunctionSignature {
} }
if !self.generic_parameters.is_empty() { if !self.generic_parameters.is_empty() {
join(self.generic_parameters.iter()) write!(f, "{}", self.generic_parameters.iter().sep_by(", ").surround_with("<", ">"))?;
.separator(", ")
.surround_with("<", ">")
.to_fmt(f)?;
} }
join(self.parameters.iter()).separator(", ").surround_with("(", ")").to_fmt(f)?; write!(f, "{}", self.parameters.iter().sep_by(", ").surround_with("(", ")"))?;
if let Some(t) = &self.ret_type { if let Some(t) = &self.ret_type {
write!(f, " -> {}", t)?; write!(f, " -> {}", t)?;
} }
if !self.where_predicates.is_empty() { if !self.where_predicates.is_empty() {
write!(f, "\nwhere ")?; write!(f, "\nwhere {}", self.where_predicates.iter().sep_by(",\n "))?;
join(self.where_predicates.iter()).separator(",\n ").to_fmt(f)?;
} }
Ok(()) Ok(())

View File

@ -1,7 +1,7 @@
//! FIXME: write short doc here //! FIXME: write short doc here
use format_buf::format;
use ra_syntax::ast::{self, AstNode, NameOwner, TypeAscriptionOwner, VisibilityOwner}; use ra_syntax::ast::{self, AstNode, NameOwner, TypeAscriptionOwner, VisibilityOwner};
use stdx::format_to;
pub(crate) trait ShortLabel { pub(crate) trait ShortLabel {
fn short_label(&self) -> Option<String>; fn short_label(&self) -> Option<String>;
@ -80,7 +80,7 @@ where
let mut buf = short_label_from_node(node, prefix)?; let mut buf = short_label_from_node(node, prefix)?;
if let Some(type_ref) = node.ascribed_type() { if let Some(type_ref) = node.ascribed_type() {
format!(buf, ": {}", type_ref.syntax()); format_to!(buf, ": {}", type_ref.syntax());
} }
Some(buf) Some(buf)

11
crates/stdx/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "stdx"
version = "0.1.0"
authors = ["rust-analyzer developers"]
edition = "2018"
[lib]
doctest = false
[dependencies]
# Think twice before adding anything here

75
crates/stdx/src/lib.rs Normal file
View File

@ -0,0 +1,75 @@
//! Missing batteries for standard libraries.
use std::{cell::Cell, fmt};
/// Appends formatted string to a `String`.
#[macro_export]
macro_rules! format_to {
(&buf:expr) => ();
($buf:expr, $lit:literal $($arg:tt)*) => {
{ use ::std::fmt::Write as _; let _ = ::std::write!($buf, $lit $($arg)*); }
};
}
pub trait SepBy: Sized {
/// Returns an `impl fmt::Display`, which joins elements via a separator.
fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self>;
}
impl<I> SepBy for I
where
I: Iterator,
I::Item: fmt::Display,
{
fn sep_by<'a>(self, sep: &'a std::primitive::str) -> SepByBuilder<'a, Self> {
SepByBuilder::new(sep, self)
}
}
pub struct SepByBuilder<'a, I> {
sep: &'a str,
prefix: &'a str,
suffix: &'a str,
iter: Cell<Option<I>>,
}
impl<'a, I> SepByBuilder<'a, I> {
fn new(sep: &'a str, iter: I) -> SepByBuilder<'a, I> {
SepByBuilder { sep, prefix: "", suffix: "", iter: Cell::new(Some(iter)) }
}
pub fn prefix(mut self, prefix: &'a str) -> Self {
self.prefix = prefix;
self
}
pub fn suffix(mut self, suffix: &'a str) -> Self {
self.suffix = suffix;
self
}
/// Set both suffix and prefix.
pub fn surround_with(self, prefix: &'a str, suffix: &'a str) -> Self {
self.prefix(prefix).suffix(suffix)
}
}
impl<I> fmt::Display for SepByBuilder<'_, I>
where
I: Iterator,
I::Item: fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.prefix)?;
let mut first = true;
for item in self.iter.take().unwrap() {
if !first {
f.write_str(self.sep)?;
}
first = false;
fmt::Display::fmt(&item, f)?;
}
f.write_str(self.suffix)?;
Ok(())
}
}