mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Merge #1545
1545: migrate ra_syntax to the new rowan API r=matklad a=matklad Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
f209843e31
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -1313,7 +1313,7 @@ dependencies = [
|
|||||||
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ra_parser 0.1.0",
|
"ra_parser 0.1.0",
|
||||||
"ra_text_edit 0.1.0",
|
"ra_text_edit 0.1.0",
|
||||||
"rowan 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rowan 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"smol_str 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"smol_str 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"test_utils 0.1.0",
|
"test_utils 0.1.0",
|
||||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -1584,11 +1584,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rowan"
|
name = "rowan"
|
||||||
version = "0.5.5"
|
version = "0.5.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"colosseum 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"colosseum 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"smol_str 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"smol_str 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"text_unit 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"text_unit 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -2275,7 +2275,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7790c7f1cc73d831d28dc5a7deb316a006e7848e6a7f467cdb10a0a9e0fb1c"
|
"checksum relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0e7790c7f1cc73d831d28dc5a7deb316a006e7848e6a7f467cdb10a0a9e0fb1c"
|
||||||
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
|
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
|
||||||
"checksum ron 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "17f52a24414403f81528b67488cf8edc4eda977d3af1646bb6b106a600ead78f"
|
"checksum ron 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "17f52a24414403f81528b67488cf8edc4eda977d3af1646bb6b106a600ead78f"
|
||||||
"checksum rowan 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "500ba7550373d42593a5228085bad391517378fa31ad2a84defe100dd8259fef"
|
"checksum rowan 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0c433ffe99ac9b96fa9882805d05eee5d750c9202fb42d0546c556e5d70d54be"
|
||||||
"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af"
|
"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af"
|
||||||
"checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8"
|
"checksum rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7540fc8b0c49f096ee9c961cda096467dce8084bec6bdca2fc83895fd9b28cb8"
|
||||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
|
@ -9,7 +9,7 @@ use crate::{Assist, AssistCtx, AssistId};
|
|||||||
|
|
||||||
pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
|
let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
|
||||||
let node_start = derive_insertion_offset(nominal)?;
|
let node_start = derive_insertion_offset(&nominal)?;
|
||||||
ctx.add_action(AssistId("add_derive"), "add `#[derive]`", |edit| {
|
ctx.add_action(AssistId("add_derive"), "add `#[derive]`", |edit| {
|
||||||
let derive_attr = nominal
|
let derive_attr = nominal
|
||||||
.attrs()
|
.attrs()
|
||||||
|
@ -27,7 +27,7 @@ pub(crate) fn add_explicit_type(mut ctx: AssistCtx<impl HirDatabase>) -> Option<
|
|||||||
// Infer type
|
// Infer type
|
||||||
let db = ctx.db;
|
let db = ctx.db;
|
||||||
let analyzer = hir::SourceAnalyzer::new(db, ctx.frange.file_id, stmt.syntax(), None);
|
let analyzer = hir::SourceAnalyzer::new(db, ctx.frange.file_id, stmt.syntax(), None);
|
||||||
let ty = analyzer.type_of(db, expr)?;
|
let ty = analyzer.type_of(db, &expr)?;
|
||||||
// Assist not applicable if the type is unknown
|
// Assist not applicable if the type is unknown
|
||||||
if is_unknown(&ty) {
|
if is_unknown(&ty) {
|
||||||
return None;
|
return None;
|
||||||
|
@ -16,7 +16,7 @@ pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
|||||||
let start_offset = nominal.syntax().range().end();
|
let start_offset = nominal.syntax().range().end();
|
||||||
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 {
|
||||||
type_params.syntax().text().push_to(&mut buf);
|
type_params.syntax().text().push_to(&mut buf);
|
||||||
}
|
}
|
||||||
buf.push_str(" ");
|
buf.push_str(" ");
|
||||||
@ -25,9 +25,9 @@ pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
|||||||
let lifetime_params = type_params
|
let lifetime_params = type_params
|
||||||
.lifetime_params()
|
.lifetime_params()
|
||||||
.filter_map(|it| it.lifetime_token())
|
.filter_map(|it| it.lifetime_token())
|
||||||
.map(|it| it.text());
|
.map(|it| it.text().clone());
|
||||||
let type_params =
|
let type_params =
|
||||||
type_params.type_params().filter_map(|it| it.name()).map(|it| it.text());
|
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);
|
join(lifetime_params.chain(type_params)).surround_with("<", ">").to_buf(&mut buf);
|
||||||
}
|
}
|
||||||
buf.push_str(" {\n");
|
buf.push_str(" {\n");
|
||||||
|
@ -5,8 +5,8 @@ use crate::{
|
|||||||
|
|
||||||
use hir::{db::HirDatabase, HasSource};
|
use hir::{db::HirDatabase, HasSource};
|
||||||
use ra_db::FilePosition;
|
use ra_db::FilePosition;
|
||||||
use ra_syntax::ast::{self, AstNode, ImplItem, ImplItemKind, NameOwner};
|
use ra_syntax::ast::{self, AstNode, ImplItemKind, NameOwner};
|
||||||
use ra_syntax::{SmolStr, TreeArc};
|
use ra_syntax::SmolStr;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
enum AddMissingImplMembersMode {
|
enum AddMissingImplMembersMode {
|
||||||
@ -46,16 +46,16 @@ fn add_missing_impl_members_inner(
|
|||||||
let position = FilePosition { file_id, offset: impl_node.syntax().range().start() };
|
let position = FilePosition { file_id, offset: impl_node.syntax().range().start() };
|
||||||
let analyzer = hir::SourceAnalyzer::new(ctx.db, position.file_id, impl_node.syntax(), None);
|
let analyzer = hir::SourceAnalyzer::new(ctx.db, position.file_id, impl_node.syntax(), None);
|
||||||
|
|
||||||
resolve_target_trait_def(ctx.db, &analyzer, impl_node)?
|
resolve_target_trait_def(ctx.db, &analyzer, &impl_node)?
|
||||||
};
|
};
|
||||||
|
|
||||||
let def_name = |kind| -> Option<&SmolStr> {
|
let def_name = |kind| -> Option<SmolStr> {
|
||||||
match kind {
|
match kind {
|
||||||
ImplItemKind::FnDef(def) => def.name(),
|
ast::ImplItemKind::FnDef(def) => def.name(),
|
||||||
ImplItemKind::TypeAliasDef(def) => def.name(),
|
ast::ImplItemKind::TypeAliasDef(def) => def.name(),
|
||||||
ImplItemKind::ConstDef(def) => def.name(),
|
ast::ImplItemKind::ConstDef(def) => def.name(),
|
||||||
}
|
}
|
||||||
.map(ast::Name::text)
|
.map(|it| it.text().clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
let trait_items = trait_def.item_list()?.impl_items();
|
let trait_items = trait_def.item_list()?.impl_items();
|
||||||
@ -78,18 +78,13 @@ fn add_missing_impl_members_inner(
|
|||||||
|
|
||||||
ctx.add_action(AssistId(assist_id), label, |edit| {
|
ctx.add_action(AssistId(assist_id), label, |edit| {
|
||||||
let n_existing_items = impl_item_list.impl_items().count();
|
let n_existing_items = impl_item_list.impl_items().count();
|
||||||
let items: Vec<_> = missing_items
|
let items = missing_items.into_iter().map(|it| match it.kind() {
|
||||||
.into_iter()
|
ImplItemKind::FnDef(def) => strip_docstring(add_body(def).into()),
|
||||||
.map(|it| match it.kind() {
|
_ => strip_docstring(it),
|
||||||
ImplItemKind::FnDef(def) => {
|
});
|
||||||
strip_docstring(ImplItem::cast(add_body(def).syntax()).unwrap())
|
|
||||||
}
|
|
||||||
_ => strip_docstring(it),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let mut ast_editor = AstEditor::new(impl_item_list);
|
let mut ast_editor = AstEditor::new(impl_item_list);
|
||||||
|
|
||||||
ast_editor.append_items(items.iter().map(|it| &**it));
|
ast_editor.append_items(items);
|
||||||
|
|
||||||
let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap();
|
let first_new_item = ast_editor.ast().impl_items().nth(n_existing_items).unwrap();
|
||||||
let cursor_position = first_new_item.syntax().range().start();
|
let cursor_position = first_new_item.syntax().range().start();
|
||||||
@ -101,14 +96,14 @@ fn add_missing_impl_members_inner(
|
|||||||
ctx.build()
|
ctx.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strip_docstring(item: &ast::ImplItem) -> TreeArc<ast::ImplItem> {
|
fn strip_docstring(item: ast::ImplItem) -> ast::ImplItem {
|
||||||
let mut ast_editor = AstEditor::new(item);
|
let mut ast_editor = AstEditor::new(item);
|
||||||
ast_editor.strip_attrs_and_docs();
|
ast_editor.strip_attrs_and_docs();
|
||||||
ast_editor.ast().to_owned()
|
ast_editor.ast().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_body(fn_def: &ast::FnDef) -> TreeArc<ast::FnDef> {
|
fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
|
||||||
let mut ast_editor = AstEditor::new(fn_def);
|
let mut ast_editor = AstEditor::new(fn_def.clone());
|
||||||
if fn_def.body().is_none() {
|
if fn_def.body().is_none() {
|
||||||
ast_editor.set_body(&AstBuilder::<ast::Block>::single_expr(
|
ast_editor.set_body(&AstBuilder::<ast::Block>::single_expr(
|
||||||
&AstBuilder::<ast::Expr>::unimplemented(),
|
&AstBuilder::<ast::Expr>::unimplemented(),
|
||||||
@ -123,9 +118,12 @@ fn resolve_target_trait_def(
|
|||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
analyzer: &hir::SourceAnalyzer,
|
analyzer: &hir::SourceAnalyzer,
|
||||||
impl_block: &ast::ImplBlock,
|
impl_block: &ast::ImplBlock,
|
||||||
) -> Option<TreeArc<ast::TraitDef>> {
|
) -> Option<ast::TraitDef> {
|
||||||
let ast_path =
|
let ast_path = impl_block
|
||||||
impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?.path()?;
|
.target_trait()
|
||||||
|
.map(|it| it.syntax().clone())
|
||||||
|
.and_then(ast::PathType::cast)?
|
||||||
|
.path()?;
|
||||||
|
|
||||||
match analyzer.resolve_path(db, &ast_path) {
|
match analyzer.resolve_path(db, &ast_path) {
|
||||||
Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).ast),
|
Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).ast),
|
||||||
|
@ -49,7 +49,7 @@ pub(crate) enum Assist {
|
|||||||
pub(crate) struct AssistCtx<'a, DB> {
|
pub(crate) struct AssistCtx<'a, DB> {
|
||||||
pub(crate) db: &'a DB,
|
pub(crate) db: &'a DB,
|
||||||
pub(crate) frange: FileRange,
|
pub(crate) frange: FileRange,
|
||||||
source_file: &'a SourceFile,
|
source_file: SourceFile,
|
||||||
should_compute_edit: bool,
|
should_compute_edit: bool,
|
||||||
assist: Assist,
|
assist: Assist,
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ impl<'a, DB> Clone for AssistCtx<'a, DB> {
|
|||||||
AssistCtx {
|
AssistCtx {
|
||||||
db: self.db,
|
db: self.db,
|
||||||
frange: self.frange,
|
frange: self.frange,
|
||||||
source_file: self.source_file,
|
source_file: self.source_file.clone(),
|
||||||
should_compute_edit: self.should_compute_edit,
|
should_compute_edit: self.should_compute_edit,
|
||||||
assist: self.assist.clone(),
|
assist: self.assist.clone(),
|
||||||
}
|
}
|
||||||
@ -104,18 +104,18 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
|
|||||||
Some(self.assist)
|
Some(self.assist)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken<'a>> {
|
pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> {
|
||||||
find_token_at_offset(self.source_file.syntax(), self.frange.range.start())
|
find_token_at_offset(self.source_file.syntax(), self.frange.range.start())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn node_at_offset<N: AstNode>(&self) -> Option<&'a N> {
|
pub(crate) fn node_at_offset<N: AstNode>(&self) -> Option<N> {
|
||||||
find_node_at_offset(self.source_file.syntax(), self.frange.range.start())
|
find_node_at_offset(self.source_file.syntax(), self.frange.range.start())
|
||||||
}
|
}
|
||||||
pub(crate) fn covering_element(&self) -> SyntaxElement<'a> {
|
pub(crate) fn covering_element(&self) -> SyntaxElement {
|
||||||
find_covering_element(self.source_file.syntax(), self.frange.range)
|
find_covering_element(self.source_file.syntax(), self.frange.range)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement<'a> {
|
pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement {
|
||||||
find_covering_element(self.source_file.syntax(), range)
|
find_covering_element(self.source_file.syntax(), range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,7 +139,7 @@ impl AssistBuilder {
|
|||||||
) {
|
) {
|
||||||
let mut replace_with = replace_with.into();
|
let mut replace_with = replace_with.into();
|
||||||
if let Some(indent) = leading_indent(node) {
|
if let Some(indent) = leading_indent(node) {
|
||||||
replace_with = reindent(&replace_with, indent)
|
replace_with = reindent(&replace_with, &indent)
|
||||||
}
|
}
|
||||||
self.replace(node.range(), replace_with)
|
self.replace(node.range(), replace_with)
|
||||||
}
|
}
|
||||||
|
@ -4,18 +4,18 @@ use arrayvec::ArrayVec;
|
|||||||
use hir::Name;
|
use hir::Name;
|
||||||
use ra_fmt::leading_indent;
|
use ra_fmt::leading_indent;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast, AstNode, Direction, InsertPosition, SourceFile, SyntaxElement, SyntaxKind::*, TreeArc, T,
|
ast, AstNode, Direction, InsertPosition, SourceFile, SyntaxElement, SyntaxKind::*, T,
|
||||||
};
|
};
|
||||||
use ra_text_edit::TextEditBuilder;
|
use ra_text_edit::TextEditBuilder;
|
||||||
|
|
||||||
pub struct AstEditor<N: AstNode> {
|
pub struct AstEditor<N: AstNode> {
|
||||||
original_ast: TreeArc<N>,
|
original_ast: N,
|
||||||
ast: TreeArc<N>,
|
ast: N,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<N: AstNode> AstEditor<N> {
|
impl<N: AstNode> AstEditor<N> {
|
||||||
pub fn new(node: &N) -> AstEditor<N> {
|
pub fn new(node: N) -> AstEditor<N> {
|
||||||
AstEditor { original_ast: node.to_owned(), ast: node.to_owned() }
|
AstEditor { original_ast: node.clone(), ast: node }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_text_edit(self, builder: &mut TextEditBuilder) {
|
pub fn into_text_edit(self, builder: &mut TextEditBuilder) {
|
||||||
@ -26,27 +26,27 @@ impl<N: AstNode> AstEditor<N> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn ast(&self) -> &N {
|
pub fn ast(&self) -> &N {
|
||||||
&*self.ast
|
&self.ast
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn insert_children<'a>(
|
fn insert_children(
|
||||||
&self,
|
&self,
|
||||||
position: InsertPosition<SyntaxElement<'_>>,
|
position: InsertPosition<SyntaxElement>,
|
||||||
to_insert: impl Iterator<Item = SyntaxElement<'a>>,
|
to_insert: impl Iterator<Item = SyntaxElement>,
|
||||||
) -> TreeArc<N> {
|
) -> N {
|
||||||
let new_syntax = self.ast().syntax().insert_children(position, to_insert);
|
let new_syntax = self.ast().syntax().insert_children(position, to_insert);
|
||||||
N::cast(&new_syntax).unwrap().to_owned()
|
N::cast(new_syntax).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn replace_children<'a>(
|
fn replace_children(
|
||||||
&self,
|
&self,
|
||||||
to_delete: RangeInclusive<SyntaxElement<'_>>,
|
to_delete: RangeInclusive<SyntaxElement>,
|
||||||
to_insert: impl Iterator<Item = SyntaxElement<'a>>,
|
to_insert: impl Iterator<Item = SyntaxElement>,
|
||||||
) -> TreeArc<N> {
|
) -> N {
|
||||||
let new_syntax = self.ast().syntax().replace_children(to_delete, to_insert);
|
let new_syntax = self.ast().syntax().replace_children(to_delete, to_insert);
|
||||||
N::cast(&new_syntax).unwrap().to_owned()
|
N::cast(new_syntax).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_make_multiline(&mut self) {
|
fn do_make_multiline(&mut self) {
|
||||||
@ -66,16 +66,18 @@ impl<N: AstNode> AstEditor<N> {
|
|||||||
if ws.text().contains('\n') {
|
if ws.text().contains('\n') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Some(ws)
|
Some(ws.clone())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let indent = leading_indent(self.ast().syntax()).unwrap_or("");
|
let indent = leading_indent(self.ast().syntax()).unwrap_or("".into());
|
||||||
let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
|
let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
|
||||||
let to_insert = iter::once(ws.ws().into());
|
let to_insert = iter::once(ws.ws().into());
|
||||||
self.ast = match existing_ws {
|
self.ast = match existing_ws {
|
||||||
None => self.insert_children(InsertPosition::After(l_curly), to_insert),
|
None => self.insert_children(InsertPosition::After(l_curly), to_insert),
|
||||||
Some(ws) => self.replace_children(RangeInclusive::new(ws.into(), ws.into()), to_insert),
|
Some(ws) => {
|
||||||
|
self.replace_children(RangeInclusive::new(ws.clone().into(), ws.into()), to_insert)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,7 +97,7 @@ impl AstEditor<ast::NamedFieldList> {
|
|||||||
let space = if is_multiline {
|
let space = if is_multiline {
|
||||||
ws = tokens::WsBuilder::new(&format!(
|
ws = tokens::WsBuilder::new(&format!(
|
||||||
"\n{} ",
|
"\n{} ",
|
||||||
leading_indent(self.ast().syntax()).unwrap_or("")
|
leading_indent(self.ast().syntax()).unwrap_or("".into())
|
||||||
));
|
));
|
||||||
ws.ws()
|
ws.ws()
|
||||||
} else {
|
} else {
|
||||||
@ -104,7 +106,7 @@ impl AstEditor<ast::NamedFieldList> {
|
|||||||
|
|
||||||
let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
|
let mut to_insert: ArrayVec<[SyntaxElement; 4]> = ArrayVec::new();
|
||||||
to_insert.push(space.into());
|
to_insert.push(space.into());
|
||||||
to_insert.push(field.syntax().into());
|
to_insert.push(field.syntax().clone().into());
|
||||||
to_insert.push(tokens::comma().into());
|
to_insert.push(tokens::comma().into());
|
||||||
|
|
||||||
macro_rules! after_l_curly {
|
macro_rules! after_l_curly {
|
||||||
@ -127,7 +129,7 @@ impl AstEditor<ast::NamedFieldList> {
|
|||||||
InsertPosition::After(comma)
|
InsertPosition::After(comma)
|
||||||
} else {
|
} else {
|
||||||
to_insert.insert(0, tokens::comma().into());
|
to_insert.insert(0, tokens::comma().into());
|
||||||
InsertPosition::After($anchor.syntax().into())
|
InsertPosition::After($anchor.syntax().clone().into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -144,7 +146,9 @@ impl AstEditor<ast::NamedFieldList> {
|
|||||||
None => after_l_curly!(),
|
None => after_l_curly!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InsertPosition::Before(anchor) => InsertPosition::Before(anchor.syntax().into()),
|
InsertPosition::Before(anchor) => {
|
||||||
|
InsertPosition::Before(anchor.syntax().clone().into())
|
||||||
|
}
|
||||||
InsertPosition::After(anchor) => after_field!(anchor),
|
InsertPosition::After(anchor) => after_field!(anchor),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -157,7 +161,7 @@ impl AstEditor<ast::NamedFieldList> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AstEditor<ast::ItemList> {
|
impl AstEditor<ast::ItemList> {
|
||||||
pub fn append_items<'a>(&mut self, items: impl Iterator<Item = &'a ast::ImplItem>) {
|
pub fn append_items(&mut self, items: impl Iterator<Item = ast::ImplItem>) {
|
||||||
let n_existing_items = self.ast().impl_items().count();
|
let n_existing_items = self.ast().impl_items().count();
|
||||||
if n_existing_items == 0 {
|
if n_existing_items == 0 {
|
||||||
self.do_make_multiline();
|
self.do_make_multiline();
|
||||||
@ -165,22 +169,23 @@ impl AstEditor<ast::ItemList> {
|
|||||||
items.for_each(|it| self.append_item(it));
|
items.for_each(|it| self.append_item(it));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn append_item(&mut self, item: &ast::ImplItem) {
|
pub fn append_item(&mut self, item: ast::ImplItem) {
|
||||||
let (indent, position) = match self.ast().impl_items().last() {
|
let (indent, position) = match self.ast().impl_items().last() {
|
||||||
Some(it) => (
|
Some(it) => (
|
||||||
leading_indent(it.syntax()).unwrap_or("").to_string(),
|
leading_indent(it.syntax()).unwrap_or_default().to_string(),
|
||||||
InsertPosition::After(it.syntax().into()),
|
InsertPosition::After(it.syntax().clone().into()),
|
||||||
),
|
),
|
||||||
None => match self.l_curly() {
|
None => match self.l_curly() {
|
||||||
Some(it) => (
|
Some(it) => (
|
||||||
" ".to_string() + leading_indent(self.ast().syntax()).unwrap_or(""),
|
" ".to_string() + &leading_indent(self.ast().syntax()).unwrap_or_default(),
|
||||||
InsertPosition::After(it),
|
InsertPosition::After(it),
|
||||||
),
|
),
|
||||||
None => return,
|
None => return,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
|
let ws = tokens::WsBuilder::new(&format!("\n{}", indent));
|
||||||
let to_insert: ArrayVec<[SyntaxElement; 2]> = [ws.ws().into(), item.syntax().into()].into();
|
let to_insert: ArrayVec<[SyntaxElement; 2]> =
|
||||||
|
[ws.ws().into(), item.syntax().clone().into()].into();
|
||||||
self.ast = self.insert_children(position, to_insert.into_iter());
|
self.ast = self.insert_children(position, to_insert.into_iter());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,9 +202,9 @@ impl AstEditor<ast::ImplItem> {
|
|||||||
.children_with_tokens()
|
.children_with_tokens()
|
||||||
.find(|it| it.kind() == ATTR || it.kind() == COMMENT)
|
.find(|it| it.kind() == ATTR || it.kind() == COMMENT)
|
||||||
{
|
{
|
||||||
let end = match start.next_sibling_or_token() {
|
let end = match &start.next_sibling_or_token() {
|
||||||
Some(el) if el.kind() == WHITESPACE => el,
|
Some(el) if el.kind() == WHITESPACE => el.clone(),
|
||||||
Some(_) | None => start,
|
Some(_) | None => start.clone(),
|
||||||
};
|
};
|
||||||
self.ast = self.replace_children(RangeInclusive::new(start, end), iter::empty());
|
self.ast = self.replace_children(RangeInclusive::new(start, end), iter::empty());
|
||||||
}
|
}
|
||||||
@ -210,18 +215,18 @@ impl AstEditor<ast::FnDef> {
|
|||||||
pub fn set_body(&mut self, body: &ast::Block) {
|
pub fn set_body(&mut self, body: &ast::Block) {
|
||||||
let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
|
let mut to_insert: ArrayVec<[SyntaxElement; 2]> = ArrayVec::new();
|
||||||
let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.ast().body() {
|
let old_body_or_semi: SyntaxElement = if let Some(old_body) = self.ast().body() {
|
||||||
old_body.syntax().into()
|
old_body.syntax().clone().into()
|
||||||
} else if let Some(semi) = self.ast().semicolon_token() {
|
} else if let Some(semi) = self.ast().semicolon_token() {
|
||||||
to_insert.push(tokens::single_space().into());
|
to_insert.push(tokens::single_space().into());
|
||||||
semi.into()
|
semi.into()
|
||||||
} else {
|
} else {
|
||||||
to_insert.push(tokens::single_space().into());
|
to_insert.push(tokens::single_space().into());
|
||||||
to_insert.push(body.syntax().into());
|
to_insert.push(body.syntax().clone().into());
|
||||||
self.ast = self.insert_children(InsertPosition::Last, to_insert.into_iter());
|
self.ast = self.insert_children(InsertPosition::Last, to_insert.into_iter());
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
to_insert.push(body.syntax().into());
|
to_insert.push(body.syntax().clone().into());
|
||||||
let replace_range = RangeInclusive::new(old_body_or_semi, old_body_or_semi);
|
let replace_range = RangeInclusive::new(old_body_or_semi.clone(), old_body_or_semi);
|
||||||
self.ast = self.replace_children(replace_range, to_insert.into_iter())
|
self.ast = self.replace_children(replace_range, to_insert.into_iter())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,15 +236,15 @@ pub struct AstBuilder<N: AstNode> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AstBuilder<ast::NamedField> {
|
impl AstBuilder<ast::NamedField> {
|
||||||
pub fn from_name(name: &Name) -> TreeArc<ast::NamedField> {
|
pub fn from_name(name: &Name) -> ast::NamedField {
|
||||||
ast_node_from_file_text(&format!("fn f() {{ S {{ {}: (), }} }}", name))
|
ast_node_from_file_text(&format!("fn f() {{ S {{ {}: (), }} }}", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_text(text: &str) -> TreeArc<ast::NamedField> {
|
fn from_text(text: &str) -> ast::NamedField {
|
||||||
ast_node_from_file_text(&format!("fn f() {{ S {{ {}, }} }}", text))
|
ast_node_from_file_text(&format!("fn f() {{ S {{ {}, }} }}", text))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_pieces(name: &ast::NameRef, expr: Option<&ast::Expr>) -> TreeArc<ast::NamedField> {
|
pub fn from_pieces(name: &ast::NameRef, expr: Option<&ast::Expr>) -> ast::NamedField {
|
||||||
match expr {
|
match expr {
|
||||||
Some(expr) => Self::from_text(&format!("{}: {}", name.syntax(), expr.syntax())),
|
Some(expr) => Self::from_text(&format!("{}: {}", name.syntax(), expr.syntax())),
|
||||||
None => Self::from_text(&name.syntax().to_string()),
|
None => Self::from_text(&name.syntax().to_string()),
|
||||||
@ -248,36 +253,36 @@ impl AstBuilder<ast::NamedField> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AstBuilder<ast::Block> {
|
impl AstBuilder<ast::Block> {
|
||||||
fn from_text(text: &str) -> TreeArc<ast::Block> {
|
fn from_text(text: &str) -> ast::Block {
|
||||||
ast_node_from_file_text(&format!("fn f() {}", text))
|
ast_node_from_file_text(&format!("fn f() {}", text))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn single_expr(e: &ast::Expr) -> TreeArc<ast::Block> {
|
pub fn single_expr(e: &ast::Expr) -> ast::Block {
|
||||||
Self::from_text(&format!("{{ {} }}", e.syntax()))
|
Self::from_text(&format!("{{ {} }}", e.syntax()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstBuilder<ast::Expr> {
|
impl AstBuilder<ast::Expr> {
|
||||||
fn from_text(text: &str) -> TreeArc<ast::Expr> {
|
fn from_text(text: &str) -> ast::Expr {
|
||||||
ast_node_from_file_text(&format!("fn f() {{ {}; }}", text))
|
ast_node_from_file_text(&format!("fn f() {{ {}; }}", text))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unit() -> TreeArc<ast::Expr> {
|
pub fn unit() -> ast::Expr {
|
||||||
Self::from_text("()")
|
Self::from_text("()")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unimplemented() -> TreeArc<ast::Expr> {
|
pub fn unimplemented() -> ast::Expr {
|
||||||
Self::from_text("unimplemented!()")
|
Self::from_text("unimplemented!()")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstBuilder<ast::NameRef> {
|
impl AstBuilder<ast::NameRef> {
|
||||||
pub fn new(text: &str) -> TreeArc<ast::NameRef> {
|
pub fn new(text: &str) -> ast::NameRef {
|
||||||
ast_node_from_file_text(&format!("fn f() {{ {}; }}", text))
|
ast_node_from_file_text(&format!("fn f() {{ {}; }}", text))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ast_node_from_file_text<N: AstNode>(text: &str) -> TreeArc<N> {
|
fn ast_node_from_file_text<N: AstNode>(text: &str) -> N {
|
||||||
let parse = SourceFile::parse(text);
|
let parse = SourceFile::parse(text);
|
||||||
let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap().to_owned();
|
let res = parse.tree().syntax().descendants().find_map(N::cast).unwrap().to_owned();
|
||||||
res
|
res
|
||||||
@ -285,47 +290,49 @@ fn ast_node_from_file_text<N: AstNode>(text: &str) -> TreeArc<N> {
|
|||||||
|
|
||||||
mod tokens {
|
mod tokens {
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use ra_syntax::{AstNode, SourceFile, SyntaxKind::*, SyntaxToken, TreeArc, T};
|
use ra_syntax::{AstNode, Parse, SourceFile, SyntaxKind::*, SyntaxToken, T};
|
||||||
|
|
||||||
static SOURCE_FILE: Lazy<TreeArc<SourceFile>> =
|
static SOURCE_FILE: Lazy<Parse<SourceFile>> = Lazy::new(|| SourceFile::parse(",\n; ;"));
|
||||||
Lazy::new(|| SourceFile::parse(",\n; ;").tree().to_owned());
|
|
||||||
|
|
||||||
pub(crate) fn comma() -> SyntaxToken<'static> {
|
pub(crate) fn comma() -> SyntaxToken {
|
||||||
SOURCE_FILE
|
SOURCE_FILE
|
||||||
|
.tree()
|
||||||
.syntax()
|
.syntax()
|
||||||
.descendants_with_tokens()
|
.descendants_with_tokens()
|
||||||
.filter_map(|it| it.as_token())
|
.filter_map(|it| it.as_token().cloned())
|
||||||
.find(|it| it.kind() == T![,])
|
.find(|it| it.kind() == T![,])
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn single_space() -> SyntaxToken<'static> {
|
pub(crate) fn single_space() -> SyntaxToken {
|
||||||
SOURCE_FILE
|
SOURCE_FILE
|
||||||
|
.tree()
|
||||||
.syntax()
|
.syntax()
|
||||||
.descendants_with_tokens()
|
.descendants_with_tokens()
|
||||||
.filter_map(|it| it.as_token())
|
.filter_map(|it| it.as_token().cloned())
|
||||||
.find(|it| it.kind() == WHITESPACE && it.text().as_str() == " ")
|
.find(|it| it.kind() == WHITESPACE && it.text().as_str() == " ")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub(crate) fn single_newline() -> SyntaxToken<'static> {
|
pub(crate) fn single_newline() -> SyntaxToken {
|
||||||
SOURCE_FILE
|
SOURCE_FILE
|
||||||
|
.tree()
|
||||||
.syntax()
|
.syntax()
|
||||||
.descendants_with_tokens()
|
.descendants_with_tokens()
|
||||||
.filter_map(|it| it.as_token())
|
.filter_map(|it| it.as_token().cloned())
|
||||||
.find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n")
|
.find(|it| it.kind() == WHITESPACE && it.text().as_str() == "\n")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct WsBuilder(TreeArc<SourceFile>);
|
pub(crate) struct WsBuilder(SourceFile);
|
||||||
|
|
||||||
impl WsBuilder {
|
impl WsBuilder {
|
||||||
pub(crate) fn new(text: &str) -> WsBuilder {
|
pub(crate) fn new(text: &str) -> WsBuilder {
|
||||||
WsBuilder(SourceFile::parse(text).ok().unwrap())
|
WsBuilder(SourceFile::parse(text).ok().unwrap())
|
||||||
}
|
}
|
||||||
pub(crate) fn ws(&self) -> SyntaxToken<'_> {
|
pub(crate) fn ws(&self) -> SyntaxToken {
|
||||||
self.0.syntax().first_child_or_token().unwrap().as_token().unwrap()
|
self.0.syntax().first_child_or_token().unwrap().as_token().cloned().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,25 +12,25 @@ use ra_syntax::{
|
|||||||
SyntaxNode, TextRange, T,
|
SyntaxNode, TextRange, T,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn collect_path_segments_raw<'a>(
|
fn collect_path_segments_raw(
|
||||||
segments: &mut Vec<&'a ast::PathSegment>,
|
segments: &mut Vec<ast::PathSegment>,
|
||||||
mut path: &'a ast::Path,
|
mut path: ast::Path,
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
let oldlen = segments.len();
|
let oldlen = segments.len();
|
||||||
loop {
|
loop {
|
||||||
let mut children = path.syntax().children_with_tokens();
|
let mut children = path.syntax().children_with_tokens();
|
||||||
let (first, second, third) = (
|
let (first, second, third) = (
|
||||||
children.next().map(|n| (n, n.kind())),
|
children.next().map(|n| (n.clone(), n.kind())),
|
||||||
children.next().map(|n| (n, n.kind())),
|
children.next().map(|n| (n.clone(), n.kind())),
|
||||||
children.next().map(|n| (n, n.kind())),
|
children.next().map(|n| (n.clone(), n.kind())),
|
||||||
);
|
);
|
||||||
match (first, second, third) {
|
match (first, second, third) {
|
||||||
(Some((subpath, PATH)), Some((_, T![::])), Some((segment, PATH_SEGMENT))) => {
|
(Some((subpath, PATH)), Some((_, T![::])), Some((segment, PATH_SEGMENT))) => {
|
||||||
path = ast::Path::cast(subpath.as_node()?)?;
|
path = ast::Path::cast(subpath.as_node()?.clone())?;
|
||||||
segments.push(ast::PathSegment::cast(segment.as_node()?)?);
|
segments.push(ast::PathSegment::cast(segment.as_node()?.clone())?);
|
||||||
}
|
}
|
||||||
(Some((segment, PATH_SEGMENT)), _, _) => {
|
(Some((segment, PATH_SEGMENT)), _, _) => {
|
||||||
segments.push(ast::PathSegment::cast(segment.as_node()?)?);
|
segments.push(ast::PathSegment::cast(segment.as_node()?.clone())?);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
(_, _, _) => return None,
|
(_, _, _) => return None,
|
||||||
@ -60,7 +60,7 @@ fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the numeber of common segments.
|
// Returns the numeber of common segments.
|
||||||
fn compare_path_segments(left: &[SmolStr], right: &[&ast::PathSegment]) -> usize {
|
fn compare_path_segments(left: &[SmolStr], right: &[ast::PathSegment]) -> usize {
|
||||||
left.iter().zip(right).filter(|(l, r)| compare_path_segment(l, r)).count()
|
left.iter().zip(right).filter(|(l, r)| compare_path_segment(l, r)).count()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,12 +81,12 @@ fn compare_path_segment_with_name(a: &SmolStr, b: &ast::Name) -> bool {
|
|||||||
a == b.text()
|
a == b.text()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Clone)]
|
||||||
enum ImportAction<'a> {
|
enum ImportAction {
|
||||||
Nothing,
|
Nothing,
|
||||||
// Add a brand new use statement.
|
// Add a brand new use statement.
|
||||||
AddNewUse {
|
AddNewUse {
|
||||||
anchor: Option<&'a SyntaxNode>, // anchor node
|
anchor: Option<SyntaxNode>, // anchor node
|
||||||
add_after_anchor: bool,
|
add_after_anchor: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -94,9 +94,9 @@ enum ImportAction<'a> {
|
|||||||
AddNestedImport {
|
AddNestedImport {
|
||||||
// how may segments matched with the target path
|
// how may segments matched with the target path
|
||||||
common_segments: usize,
|
common_segments: usize,
|
||||||
path_to_split: &'a ast::Path,
|
path_to_split: ast::Path,
|
||||||
// the first segment of path_to_split we want to add into the new nested list
|
// the first segment of path_to_split we want to add into the new nested list
|
||||||
first_segment_to_split: Option<&'a ast::PathSegment>,
|
first_segment_to_split: Option<ast::PathSegment>,
|
||||||
// Wether to add 'self' in addition to the target path
|
// Wether to add 'self' in addition to the target path
|
||||||
add_self: bool,
|
add_self: bool,
|
||||||
},
|
},
|
||||||
@ -104,20 +104,20 @@ enum ImportAction<'a> {
|
|||||||
AddInTreeList {
|
AddInTreeList {
|
||||||
common_segments: usize,
|
common_segments: usize,
|
||||||
// The UseTreeList where to add the target path
|
// The UseTreeList where to add the target path
|
||||||
tree_list: &'a ast::UseTreeList,
|
tree_list: ast::UseTreeList,
|
||||||
add_self: bool,
|
add_self: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ImportAction<'a> {
|
impl ImportAction {
|
||||||
fn add_new_use(anchor: Option<&'a SyntaxNode>, add_after_anchor: bool) -> Self {
|
fn add_new_use(anchor: Option<SyntaxNode>, add_after_anchor: bool) -> Self {
|
||||||
ImportAction::AddNewUse { anchor, add_after_anchor }
|
ImportAction::AddNewUse { anchor, add_after_anchor }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_nested_import(
|
fn add_nested_import(
|
||||||
common_segments: usize,
|
common_segments: usize,
|
||||||
path_to_split: &'a ast::Path,
|
path_to_split: ast::Path,
|
||||||
first_segment_to_split: Option<&'a ast::PathSegment>,
|
first_segment_to_split: Option<ast::PathSegment>,
|
||||||
add_self: bool,
|
add_self: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ImportAction::AddNestedImport {
|
ImportAction::AddNestedImport {
|
||||||
@ -130,14 +130,14 @@ impl<'a> ImportAction<'a> {
|
|||||||
|
|
||||||
fn add_in_tree_list(
|
fn add_in_tree_list(
|
||||||
common_segments: usize,
|
common_segments: usize,
|
||||||
tree_list: &'a ast::UseTreeList,
|
tree_list: ast::UseTreeList,
|
||||||
add_self: bool,
|
add_self: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
ImportAction::AddInTreeList { common_segments, tree_list, add_self }
|
ImportAction::AddInTreeList { common_segments, tree_list, add_self }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn better<'b>(left: &'b ImportAction<'a>, right: &'b ImportAction<'a>) -> &'b ImportAction<'a> {
|
fn better(left: ImportAction, right: ImportAction) -> ImportAction {
|
||||||
if left.is_better(right) {
|
if left.is_better(&right) {
|
||||||
left
|
left
|
||||||
} else {
|
} else {
|
||||||
right
|
right
|
||||||
@ -166,12 +166,12 @@ impl<'a> ImportAction<'a> {
|
|||||||
|
|
||||||
// Find out the best ImportAction to import target path against current_use_tree.
|
// Find out the best ImportAction to import target path against current_use_tree.
|
||||||
// If current_use_tree has a nested import the function gets called recursively on every UseTree inside a UseTreeList.
|
// If current_use_tree has a nested import the function gets called recursively on every UseTree inside a UseTreeList.
|
||||||
fn walk_use_tree_for_best_action<'a>(
|
fn walk_use_tree_for_best_action(
|
||||||
current_path_segments: &mut Vec<&'a ast::PathSegment>, // buffer containing path segments
|
current_path_segments: &mut Vec<ast::PathSegment>, // buffer containing path segments
|
||||||
current_parent_use_tree_list: Option<&'a ast::UseTreeList>, // will be Some value if we are in a nested import
|
current_parent_use_tree_list: Option<ast::UseTreeList>, // will be Some value if we are in a nested import
|
||||||
current_use_tree: &'a ast::UseTree, // the use tree we are currently examinating
|
current_use_tree: ast::UseTree, // the use tree we are currently examinating
|
||||||
target: &[SmolStr], // the path we want to import
|
target: &[SmolStr], // the path we want to import
|
||||||
) -> ImportAction<'a> {
|
) -> ImportAction {
|
||||||
// We save the number of segments in the buffer so we can restore the correct segments
|
// We save the number of segments in the buffer so we can restore the correct segments
|
||||||
// before returning. Recursive call will add segments so we need to delete them.
|
// before returning. Recursive call will add segments so we need to delete them.
|
||||||
let prev_len = current_path_segments.len();
|
let prev_len = current_path_segments.len();
|
||||||
@ -188,32 +188,36 @@ fn walk_use_tree_for_best_action<'a>(
|
|||||||
.syntax()
|
.syntax()
|
||||||
.ancestors()
|
.ancestors()
|
||||||
.find_map(ast::UseItem::cast)
|
.find_map(ast::UseItem::cast)
|
||||||
.map(AstNode::syntax),
|
.map(|it| it.syntax().clone()),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This can happen only if current_use_tree is a direct child of a UseItem
|
// This can happen only if current_use_tree is a direct child of a UseItem
|
||||||
if let Some(name) = alias.and_then(ast::NameOwner::name) {
|
if let Some(name) = alias.and_then(|it| it.name()) {
|
||||||
if compare_path_segment_with_name(&target[0], name) {
|
if compare_path_segment_with_name(&target[0], &name) {
|
||||||
return ImportAction::Nothing;
|
return ImportAction::Nothing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
collect_path_segments_raw(current_path_segments, path);
|
collect_path_segments_raw(current_path_segments, path.clone());
|
||||||
|
|
||||||
// We compare only the new segments added in the line just above.
|
// We compare only the new segments added in the line just above.
|
||||||
// The first prev_len segments were already compared in 'parent' recursive calls.
|
// The first prev_len segments were already compared in 'parent' recursive calls.
|
||||||
let left = target.split_at(prev_len).1;
|
let left = target.split_at(prev_len).1;
|
||||||
let right = current_path_segments.split_at(prev_len).1;
|
let right = current_path_segments.split_at(prev_len).1;
|
||||||
let common = compare_path_segments(left, right);
|
let common = compare_path_segments(left, &right);
|
||||||
let mut action = match common {
|
let mut action = match common {
|
||||||
0 => ImportAction::add_new_use(
|
0 => ImportAction::add_new_use(
|
||||||
// e.g: target is std::fmt and we can have
|
// e.g: target is std::fmt and we can have
|
||||||
// use foo::bar
|
// use foo::bar
|
||||||
// We add a brand new use statement
|
// We add a brand new use statement
|
||||||
current_use_tree.syntax().ancestors().find_map(ast::UseItem::cast).map(AstNode::syntax),
|
current_use_tree
|
||||||
|
.syntax()
|
||||||
|
.ancestors()
|
||||||
|
.find_map(ast::UseItem::cast)
|
||||||
|
.map(|it| it.syntax().clone()),
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
common if common == left.len() && left.len() == right.len() => {
|
common if common == left.len() && left.len() == right.len() => {
|
||||||
@ -223,9 +227,9 @@ fn walk_use_tree_for_best_action<'a>(
|
|||||||
if let Some(list) = tree_list {
|
if let Some(list) = tree_list {
|
||||||
// In case 2 we need to add self to the nested list
|
// In case 2 we need to add self to the nested list
|
||||||
// unless it's already there
|
// unless it's already there
|
||||||
let has_self = list.use_trees().map(ast::UseTree::path).any(|p| {
|
let has_self = list.use_trees().map(|it| it.path()).any(|p| {
|
||||||
p.and_then(ast::Path::segment)
|
p.and_then(|it| it.segment())
|
||||||
.and_then(ast::PathSegment::kind)
|
.and_then(|it| it.kind())
|
||||||
.filter(|k| *k == ast::PathSegmentKind::SelfKw)
|
.filter(|k| *k == ast::PathSegmentKind::SelfKw)
|
||||||
.is_some()
|
.is_some()
|
||||||
});
|
});
|
||||||
@ -248,7 +252,7 @@ fn walk_use_tree_for_best_action<'a>(
|
|||||||
ImportAction::add_nested_import(
|
ImportAction::add_nested_import(
|
||||||
prev_len + common,
|
prev_len + common,
|
||||||
path,
|
path,
|
||||||
Some(segments_to_split[0]),
|
Some(segments_to_split[0].clone()),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -263,14 +267,18 @@ fn walk_use_tree_for_best_action<'a>(
|
|||||||
.syntax()
|
.syntax()
|
||||||
.ancestors()
|
.ancestors()
|
||||||
.find_map(ast::UseItem::cast)
|
.find_map(ast::UseItem::cast)
|
||||||
.map(AstNode::syntax),
|
.map(|it| it.syntax().clone()),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
if let Some(list) = tree_list {
|
if let Some(list) = tree_list {
|
||||||
// Case 2, check recursively if the path is already imported in the nested list
|
// Case 2, check recursively if the path is already imported in the nested list
|
||||||
for u in list.use_trees() {
|
for u in list.use_trees() {
|
||||||
let child_action =
|
let child_action = walk_use_tree_for_best_action(
|
||||||
walk_use_tree_for_best_action(current_path_segments, Some(list), u, target);
|
current_path_segments,
|
||||||
|
Some(list.clone()),
|
||||||
|
u,
|
||||||
|
target,
|
||||||
|
);
|
||||||
if child_action.is_better(&better_action) {
|
if child_action.is_better(&better_action) {
|
||||||
better_action = child_action;
|
better_action = child_action;
|
||||||
if let ImportAction::Nothing = better_action {
|
if let ImportAction::Nothing = better_action {
|
||||||
@ -291,7 +299,7 @@ fn walk_use_tree_for_best_action<'a>(
|
|||||||
ImportAction::add_nested_import(
|
ImportAction::add_nested_import(
|
||||||
prev_len + common,
|
prev_len + common,
|
||||||
path,
|
path,
|
||||||
Some(segments_to_split[0]),
|
Some(segments_to_split[0].clone()),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -302,7 +310,7 @@ fn walk_use_tree_for_best_action<'a>(
|
|||||||
ImportAction::add_nested_import(
|
ImportAction::add_nested_import(
|
||||||
prev_len + common,
|
prev_len + common,
|
||||||
path,
|
path,
|
||||||
Some(segments_to_split[0]),
|
Some(segments_to_split[0].clone()),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -311,7 +319,7 @@ fn walk_use_tree_for_best_action<'a>(
|
|||||||
|
|
||||||
// If we are inside a UseTreeList adding a use statement become adding to the existing
|
// If we are inside a UseTreeList adding a use statement become adding to the existing
|
||||||
// tree list.
|
// tree list.
|
||||||
action = match (current_parent_use_tree_list, action) {
|
action = match (current_parent_use_tree_list, action.clone()) {
|
||||||
(Some(use_tree_list), ImportAction::AddNewUse { .. }) => {
|
(Some(use_tree_list), ImportAction::AddNewUse { .. }) => {
|
||||||
ImportAction::add_in_tree_list(prev_len, use_tree_list, false)
|
ImportAction::add_in_tree_list(prev_len, use_tree_list, false)
|
||||||
}
|
}
|
||||||
@ -323,19 +331,20 @@ fn walk_use_tree_for_best_action<'a>(
|
|||||||
action
|
action
|
||||||
}
|
}
|
||||||
|
|
||||||
fn best_action_for_target<'b, 'a: 'b>(
|
fn best_action_for_target(
|
||||||
container: &'a SyntaxNode,
|
container: SyntaxNode,
|
||||||
anchor: &'a SyntaxNode,
|
anchor: SyntaxNode,
|
||||||
target: &'b [SmolStr],
|
target: &[SmolStr],
|
||||||
) -> ImportAction<'a> {
|
) -> ImportAction {
|
||||||
let mut storage = Vec::with_capacity(16); // this should be the only allocation
|
let mut storage = Vec::with_capacity(16); // this should be the only allocation
|
||||||
let best_action = container
|
let best_action = container
|
||||||
.children()
|
.children()
|
||||||
.filter_map(ast::UseItem::cast)
|
.filter_map(ast::UseItem::cast)
|
||||||
.filter_map(ast::UseItem::use_tree)
|
.filter_map(|it| it.use_tree())
|
||||||
.map(|u| walk_use_tree_for_best_action(&mut storage, None, u, target))
|
.map(|u| walk_use_tree_for_best_action(&mut storage, None, u, target))
|
||||||
.fold(None, |best, a| {
|
.fold(None, |best, a| match best {
|
||||||
best.and_then(|best| Some(*ImportAction::better(&best, &a))).or_else(|| Some(a))
|
Some(best) => Some(ImportAction::better(best, a)),
|
||||||
|
None => Some(a),
|
||||||
});
|
});
|
||||||
|
|
||||||
match best_action {
|
match best_action {
|
||||||
@ -386,7 +395,7 @@ fn make_assist(action: &ImportAction, target: &[SmolStr], edit: &mut TextEditBui
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn make_assist_add_new_use(
|
fn make_assist_add_new_use(
|
||||||
anchor: &Option<&SyntaxNode>,
|
anchor: &Option<SyntaxNode>,
|
||||||
after: bool,
|
after: bool,
|
||||||
target: &[SmolStr],
|
target: &[SmolStr],
|
||||||
edit: &mut TextEditBuilder,
|
edit: &mut TextEditBuilder,
|
||||||
@ -396,7 +405,7 @@ fn make_assist_add_new_use(
|
|||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
if after {
|
if after {
|
||||||
buf.push_str("\n");
|
buf.push_str("\n");
|
||||||
if let Some(spaces) = indent {
|
if let Some(spaces) = &indent {
|
||||||
buf.push_str(spaces);
|
buf.push_str(spaces);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -405,8 +414,8 @@ fn make_assist_add_new_use(
|
|||||||
buf.push_str(";");
|
buf.push_str(";");
|
||||||
if !after {
|
if !after {
|
||||||
buf.push_str("\n\n");
|
buf.push_str("\n\n");
|
||||||
if let Some(spaces) = indent {
|
if let Some(spaces) = &indent {
|
||||||
buf.push_str(spaces);
|
buf.push_str(&spaces);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let position = if after { anchor.range().end() } else { anchor.range().start() };
|
let position = if after { anchor.range().end() } else { anchor.range().start() };
|
||||||
@ -444,7 +453,7 @@ fn make_assist_add_in_tree_list(
|
|||||||
|
|
||||||
fn make_assist_add_nested_import(
|
fn make_assist_add_nested_import(
|
||||||
path: &ast::Path,
|
path: &ast::Path,
|
||||||
first_segment_to_split: &Option<&ast::PathSegment>,
|
first_segment_to_split: &Option<ast::PathSegment>,
|
||||||
target: &[SmolStr],
|
target: &[SmolStr],
|
||||||
add_self: bool,
|
add_self: bool,
|
||||||
edit: &mut TextEditBuilder,
|
edit: &mut TextEditBuilder,
|
||||||
@ -482,7 +491,7 @@ fn apply_auto_import(
|
|||||||
target: &[SmolStr],
|
target: &[SmolStr],
|
||||||
edit: &mut TextEditBuilder,
|
edit: &mut TextEditBuilder,
|
||||||
) {
|
) {
|
||||||
let action = best_action_for_target(container, path.syntax(), target);
|
let action = best_action_for_target(container.clone(), path.syntax().clone(), target);
|
||||||
make_assist(&action, target, edit);
|
make_assist(&action, target, edit);
|
||||||
if let Some(last) = path.segment() {
|
if let Some(last) = path.segment() {
|
||||||
// Here we are assuming the assist will provide a correct use statement
|
// Here we are assuming the assist will provide a correct use statement
|
||||||
@ -522,26 +531,26 @@ pub fn auto_import_text_edit(
|
|||||||
edit: &mut TextEditBuilder,
|
edit: &mut TextEditBuilder,
|
||||||
) {
|
) {
|
||||||
let container = position.ancestors().find_map(|n| {
|
let container = position.ancestors().find_map(|n| {
|
||||||
if let Some(module) = ast::Module::cast(n) {
|
if let Some(module) = ast::Module::cast(n.clone()) {
|
||||||
return module.item_list().map(ast::AstNode::syntax);
|
return module.item_list().map(|it| it.syntax().clone());
|
||||||
}
|
}
|
||||||
ast::SourceFile::cast(n).map(ast::AstNode::syntax)
|
ast::SourceFile::cast(n).map(|it| it.syntax().clone())
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(container) = container {
|
if let Some(container) = container {
|
||||||
let action = best_action_for_target(container, anchor, target);
|
let action = best_action_for_target(container, anchor.clone(), target);
|
||||||
make_assist(&action, target, edit);
|
make_assist(&action, target, edit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let path: &ast::Path = ctx.node_at_offset()?;
|
let path: ast::Path = ctx.node_at_offset()?;
|
||||||
// We don't want to mess with use statements
|
// We don't want to mess with use statements
|
||||||
if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
|
if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let hir_path = hir::Path::from_ast(path)?;
|
let hir_path = hir::Path::from_ast(path.clone())?;
|
||||||
let segments = collect_hir_path_segments(&hir_path);
|
let segments = collect_hir_path_segments(&hir_path);
|
||||||
if segments.len() < 2 {
|
if segments.len() < 2 {
|
||||||
return None;
|
return None;
|
||||||
@ -554,7 +563,7 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
|
|||||||
format!("import {} in mod {}", fmt_segments(&segments), name.text()),
|
format!("import {} in mod {}", fmt_segments(&segments), name.text()),
|
||||||
|edit| {
|
|edit| {
|
||||||
let mut text_edit = TextEditBuilder::default();
|
let mut text_edit = TextEditBuilder::default();
|
||||||
apply_auto_import(item_list.syntax(), path, &segments, &mut text_edit);
|
apply_auto_import(item_list.syntax(), &path, &segments, &mut text_edit);
|
||||||
edit.set_edit_builder(text_edit);
|
edit.set_edit_builder(text_edit);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -566,7 +575,7 @@ pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
|
|||||||
format!("import {} in the current file", fmt_segments(&segments)),
|
format!("import {} in the current file", fmt_segments(&segments)),
|
||||||
|edit| {
|
|edit| {
|
||||||
let mut text_edit = TextEditBuilder::default();
|
let mut text_edit = TextEditBuilder::default();
|
||||||
apply_auto_import(current_file.syntax(), path, &segments, &mut text_edit);
|
apply_auto_import(current_file.syntax(), &path, &segments, &mut text_edit);
|
||||||
edit.set_edit_builder(text_edit);
|
edit.set_edit_builder(text_edit);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -35,7 +35,7 @@ fn add_vis(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
|||||||
if parent.children().any(|child| child.kind() == VISIBILITY) {
|
if parent.children().any(|child| child.kind() == VISIBILITY) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
(vis_offset(parent), keyword.range())
|
(vis_offset(&parent), keyword.range())
|
||||||
} else {
|
} else {
|
||||||
let ident = ctx.token_at_offset().find(|leaf| leaf.kind() == IDENT)?;
|
let ident = ctx.token_at_offset().find(|leaf| leaf.kind() == IDENT)?;
|
||||||
let field = ident.parent().ancestors().find_map(ast::NamedFieldDef::cast)?;
|
let field = ident.parent().ancestors().find_map(ast::NamedFieldDef::cast)?;
|
||||||
@ -65,7 +65,7 @@ fn vis_offset(node: &SyntaxNode) -> TextUnit {
|
|||||||
.unwrap_or_else(|| node.range().start())
|
.unwrap_or_else(|| node.range().start())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn change_vis(mut ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option<Assist> {
|
fn change_vis(mut ctx: AssistCtx<impl HirDatabase>, vis: ast::Visibility) -> Option<Assist> {
|
||||||
if vis.syntax().text() == "pub" {
|
if vis.syntax().text() == "pub" {
|
||||||
ctx.add_action(AssistId("change_visibility"), "change to pub(crate)", |edit| {
|
ctx.add_action(AssistId("change_visibility"), "change to pub(crate)", |edit| {
|
||||||
edit.target(vis.syntax().range());
|
edit.target(vis.syntax().range());
|
||||||
|
@ -27,7 +27,7 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
|
|||||||
let mut arm_iter = arm_list.arms();
|
let mut arm_iter = arm_list.arms();
|
||||||
let first = arm_iter.next();
|
let first = arm_iter.next();
|
||||||
|
|
||||||
match first {
|
match &first {
|
||||||
// If there arm list is empty or there is only one trivial arm, then proceed.
|
// If there arm list is empty or there is only one trivial arm, then proceed.
|
||||||
Some(arm) if is_trivial_arm(arm) => {
|
Some(arm) if is_trivial_arm(arm) => {
|
||||||
if arm_iter.next() != None {
|
if arm_iter.next() != None {
|
||||||
@ -44,7 +44,7 @@ pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<As
|
|||||||
|
|
||||||
let expr = match_expr.expr()?;
|
let expr = match_expr.expr()?;
|
||||||
let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, expr.syntax(), None);
|
let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, expr.syntax(), None);
|
||||||
let match_expr_ty = analyzer.type_of(ctx.db, expr)?;
|
let match_expr_ty = analyzer.type_of(ctx.db, &expr)?;
|
||||||
let enum_def = analyzer.autoderef(ctx.db, match_expr_ty).find_map(|ty| match ty.as_adt() {
|
let enum_def = analyzer.autoderef(ctx.db, match_expr_ty).find_map(|ty| match ty.as_adt() {
|
||||||
Some((AdtDef::Enum(e), _)) => Some(e),
|
Some((AdtDef::Enum(e), _)) => Some(e),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -6,8 +6,8 @@ use crate::{Assist, AssistCtx, AssistId};
|
|||||||
/// Flip binary expression assist.
|
/// Flip binary expression assist.
|
||||||
pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn flip_binexpr(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let expr = ctx.node_at_offset::<BinExpr>()?;
|
let expr = ctx.node_at_offset::<BinExpr>()?;
|
||||||
let lhs = expr.lhs()?.syntax();
|
let lhs = expr.lhs()?.syntax().clone();
|
||||||
let rhs = expr.rhs()?.syntax();
|
let rhs = expr.rhs()?.syntax().clone();
|
||||||
let op_range = expr.op_token()?.range();
|
let op_range = expr.op_token()?.range();
|
||||||
// The assist should be applied only if the cursor is on the operator
|
// The assist should be applied only if the cursor is on the operator
|
||||||
let cursor_in_range = ctx.frange.range.is_subrange(&op_range);
|
let cursor_in_range = ctx.frange.range.is_subrange(&op_range);
|
||||||
|
@ -5,8 +5,8 @@ use crate::{Assist, AssistCtx, AssistId};
|
|||||||
|
|
||||||
pub(crate) fn flip_comma(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn flip_comma(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let comma = ctx.token_at_offset().find(|leaf| leaf.kind() == T![,])?;
|
let comma = ctx.token_at_offset().find(|leaf| leaf.kind() == T![,])?;
|
||||||
let prev = non_trivia_sibling(comma.into(), Direction::Prev)?;
|
let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?;
|
||||||
let next = non_trivia_sibling(comma.into(), Direction::Next)?;
|
let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?;
|
||||||
ctx.add_action(AssistId("flip_comma"), "flip comma", |edit| {
|
ctx.add_action(AssistId("flip_comma"), "flip comma", |edit| {
|
||||||
edit.target(comma.range());
|
edit.target(comma.range());
|
||||||
edit.replace(prev.range(), next.to_string());
|
edit.replace(prev.range(), next.to_string());
|
||||||
|
@ -16,18 +16,18 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Opt
|
|||||||
if bind_pat.is_mutable() {
|
if bind_pat.is_mutable() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let initializer_expr = let_stmt.initializer();
|
let initializer_expr = let_stmt.initializer()?;
|
||||||
let delete_range = if let Some(whitespace) = let_stmt
|
let delete_range = if let Some(whitespace) = let_stmt
|
||||||
.syntax()
|
.syntax()
|
||||||
.next_sibling_or_token()
|
.next_sibling_or_token()
|
||||||
.and_then(|it| ast::Whitespace::cast(it.as_token()?))
|
.and_then(|it| ast::Whitespace::cast(it.as_token()?.clone()))
|
||||||
{
|
{
|
||||||
TextRange::from_to(let_stmt.syntax().range().start(), whitespace.syntax().range().end())
|
TextRange::from_to(let_stmt.syntax().range().start(), whitespace.syntax().range().end())
|
||||||
} else {
|
} else {
|
||||||
let_stmt.syntax().range()
|
let_stmt.syntax().range()
|
||||||
};
|
};
|
||||||
let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, bind_pat.syntax(), None);
|
let analyzer = hir::SourceAnalyzer::new(ctx.db, ctx.frange.file_id, bind_pat.syntax(), None);
|
||||||
let refs = analyzer.find_all_refs(bind_pat);
|
let refs = analyzer.find_all_refs(&bind_pat);
|
||||||
|
|
||||||
let mut wrap_in_parens = vec![true; refs.len()];
|
let mut wrap_in_parens = vec![true; refs.len()];
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Opt
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
wrap_in_parens[i] = match (initializer_expr?.kind(), usage_parent.kind()) {
|
wrap_in_parens[i] = match (initializer_expr.kind(), usage_parent.kind()) {
|
||||||
(ExprKind::CallExpr(_), _)
|
(ExprKind::CallExpr(_), _)
|
||||||
| (ExprKind::IndexExpr(_), _)
|
| (ExprKind::IndexExpr(_), _)
|
||||||
| (ExprKind::MethodCallExpr(_), _)
|
| (ExprKind::MethodCallExpr(_), _)
|
||||||
@ -71,7 +71,7 @@ pub(crate) fn inline_local_varialbe(mut ctx: AssistCtx<impl HirDatabase>) -> Opt
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let init_str = initializer_expr?.syntax().text().to_string();
|
let init_str = initializer_expr.syntax().text().to_string();
|
||||||
let init_in_paren = format!("({})", &init_str);
|
let init_in_paren = format!("({})", &init_str);
|
||||||
|
|
||||||
ctx.add_action(
|
ctx.add_action(
|
||||||
|
@ -20,8 +20,8 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let expr = node.ancestors().find_map(valid_target_expr)?;
|
let expr = node.ancestors().find_map(valid_target_expr)?;
|
||||||
let (anchor_stmt, wrap_in_block) = anchor_stmt(expr)?;
|
let (anchor_stmt, wrap_in_block) = anchor_stmt(expr.clone())?;
|
||||||
let indent = anchor_stmt.prev_sibling_or_token()?.as_token()?;
|
let indent = anchor_stmt.prev_sibling_or_token()?.as_token()?.clone();
|
||||||
if indent.kind() != WHITESPACE {
|
if indent.kind() != WHITESPACE {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -37,9 +37,9 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
|
|||||||
};
|
};
|
||||||
|
|
||||||
expr.syntax().text().push_to(&mut buf);
|
expr.syntax().text().push_to(&mut buf);
|
||||||
let full_stmt = ast::ExprStmt::cast(anchor_stmt);
|
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()) == expr_stmt.expr().map(|e| e.syntax())
|
Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
@ -81,7 +81,7 @@ pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option
|
|||||||
|
|
||||||
/// Check whether the node is a valid expression which can be extracted to a variable.
|
/// Check whether the node is a valid expression which can be extracted to a variable.
|
||||||
/// In general that's true for any expression, but in some cases that would produce invalid code.
|
/// In general that's true for any expression, but in some cases that would produce invalid code.
|
||||||
fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> {
|
fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
PATH_EXPR => None,
|
PATH_EXPR => None,
|
||||||
BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()),
|
BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()),
|
||||||
@ -96,14 +96,10 @@ fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> {
|
|||||||
/// to produce correct code.
|
/// to produce correct code.
|
||||||
/// It can be a statement, the last in a block expression or a wanna be block
|
/// It can be a statement, the last in a block expression or a wanna be block
|
||||||
/// expression like a lambda or match arm.
|
/// expression like a lambda or match arm.
|
||||||
fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> {
|
fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
|
||||||
expr.syntax().ancestors().find_map(|node| {
|
expr.syntax().ancestors().find_map(|node| {
|
||||||
if ast::Stmt::cast(node).is_some() {
|
|
||||||
return Some((node, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(expr) = node.parent().and_then(ast::Block::cast).and_then(|it| it.expr()) {
|
if let Some(expr) = node.parent().and_then(ast::Block::cast).and_then(|it| it.expr()) {
|
||||||
if expr.syntax() == node {
|
if expr.syntax() == &node {
|
||||||
tested_by!(test_introduce_var_last_expr);
|
tested_by!(test_introduce_var_last_expr);
|
||||||
return Some((node, false));
|
return Some((node, false));
|
||||||
}
|
}
|
||||||
@ -115,6 +111,10 @@ fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ast::Stmt::cast(node.clone()).is_some() {
|
||||||
|
return Some((node, false));
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,9 @@ pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Op
|
|||||||
|
|
||||||
ctx.add_action(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| {
|
ctx.add_action(AssistId("move_guard_to_arm_body"), "move guard to arm body", |edit| {
|
||||||
edit.target(guard.syntax().range());
|
edit.target(guard.syntax().range());
|
||||||
let offseting_amount = match space_before_guard {
|
let offseting_amount = match &space_before_guard {
|
||||||
Some(SyntaxElement::Token(tok)) => {
|
Some(SyntaxElement::Token(tok)) => {
|
||||||
if let Some(_) = ast::Whitespace::cast(tok) {
|
if let Some(_) = ast::Whitespace::cast(tok.clone()) {
|
||||||
let ele = space_before_guard.unwrap().range();
|
let ele = space_before_guard.unwrap().range();
|
||||||
edit.delete(ele);
|
edit.delete(ele);
|
||||||
ele.len()
|
ele.len()
|
||||||
@ -39,11 +39,11 @@ pub(crate) fn move_guard_to_arm_body(mut ctx: AssistCtx<impl HirDatabase>) -> Op
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let match_arm: &MatchArm = ctx.node_at_offset::<MatchArm>()?;
|
let match_arm: MatchArm = ctx.node_at_offset::<MatchArm>()?;
|
||||||
let last_match_pat = match_arm.pats().last()?;
|
let last_match_pat = match_arm.pats().last()?;
|
||||||
|
|
||||||
let arm_body = match_arm.expr()?;
|
let arm_body = match_arm.expr()?;
|
||||||
let if_expr: &IfExpr = IfExpr::cast(arm_body.syntax())?;
|
let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone())?;
|
||||||
let cond = if_expr.condition()?;
|
let cond = if_expr.condition()?;
|
||||||
let then_block = if_expr.then_branch()?;
|
let then_block = if_expr.then_branch()?;
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ pub(crate) fn move_arm_cond_to_match_guard(mut ctx: AssistCtx<impl HirDatabase>)
|
|||||||
edit.target(if_expr.syntax().range());
|
edit.target(if_expr.syntax().range());
|
||||||
let then_only_expr = then_block.statements().next().is_none();
|
let then_only_expr = then_block.statements().next().is_none();
|
||||||
|
|
||||||
match then_block.expr() {
|
match &then_block.expr() {
|
||||||
Some(then_expr) if then_only_expr => {
|
Some(then_expr) if then_only_expr => {
|
||||||
edit.replace(if_expr.syntax().range(), then_expr.syntax().text())
|
edit.replace(if_expr.syntax().range(), then_expr.syntax().text())
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use ra_syntax::{
|
|||||||
pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let macro_call = ctx.node_at_offset::<ast::MacroCall>()?;
|
let macro_call = ctx.node_at_offset::<ast::MacroCall>()?;
|
||||||
|
|
||||||
if !is_valid_macrocall(macro_call, "dbg")? {
|
if !is_valid_macrocall(¯o_call, "dbg")? {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist>
|
|||||||
};
|
};
|
||||||
|
|
||||||
let macro_content = {
|
let macro_content = {
|
||||||
let macro_args = macro_call.token_tree()?.syntax();
|
let macro_args = macro_call.token_tree()?.syntax().clone();
|
||||||
let range = macro_args.range();
|
let range = macro_args.range();
|
||||||
let start = range.start() + TextUnit::of_char('(');
|
let start = range.start() + TextUnit::of_char('(');
|
||||||
let end = range.end() - TextUnit::of_char(')');
|
let end = range.end() - TextUnit::of_char(')');
|
||||||
@ -65,7 +65,7 @@ fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<b
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let node = macro_call.token_tree()?.syntax();
|
let node = macro_call.token_tree()?.syntax().clone();
|
||||||
let first_child = node.first_child_or_token()?;
|
let first_child = node.first_child_or_token()?;
|
||||||
let last_child = node.last_child_or_token()?;
|
let last_child = node.last_child_or_token()?;
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use ra_syntax::{ast, AstNode};
|
|||||||
use crate::{Assist, AssistCtx, AssistId};
|
use crate::{Assist, AssistCtx, AssistId};
|
||||||
|
|
||||||
pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
|
||||||
let if_expr: &ast::IfExpr = ctx.node_at_offset()?;
|
let if_expr: ast::IfExpr = ctx.node_at_offset()?;
|
||||||
let cond = if_expr.condition()?;
|
let cond = if_expr.condition()?;
|
||||||
let pat = cond.pat()?;
|
let pat = cond.pat()?;
|
||||||
let expr = cond.expr()?;
|
let expr = cond.expr()?;
|
||||||
@ -25,16 +25,11 @@ pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) ->
|
|||||||
ctx.build()
|
ctx.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_match_expr(
|
fn build_match_expr(expr: ast::Expr, pat1: ast::Pat, arm1: ast::Block, arm2: ast::Block) -> String {
|
||||||
expr: &ast::Expr,
|
|
||||||
pat1: &ast::Pat,
|
|
||||||
arm1: &ast::Block,
|
|
||||||
arm2: &ast::Block,
|
|
||||||
) -> String {
|
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
buf.push_str(&format!("match {} {{\n", expr.syntax().text()));
|
buf.push_str(&format!("match {} {{\n", expr.syntax().text()));
|
||||||
buf.push_str(&format!(" {} => {}\n", pat1.syntax().text(), format_arm(arm1)));
|
buf.push_str(&format!(" {} => {}\n", pat1.syntax().text(), format_arm(&arm1)));
|
||||||
buf.push_str(&format!(" _ => {}\n", format_arm(arm2)));
|
buf.push_str(&format!(" _ => {}\n", format_arm(&arm2)));
|
||||||
buf.push_str("}");
|
buf.push_str("}");
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use clap::{App, Arg, SubCommand};
|
|||||||
use flexi_logger::Logger;
|
use flexi_logger::Logger;
|
||||||
use ra_ide_api::{file_structure, Analysis};
|
use ra_ide_api::{file_structure, Analysis};
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_syntax::{AstNode, SourceFile, TreeArc};
|
use ra_syntax::{AstNode, SourceFile};
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;
|
type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;
|
||||||
|
|
||||||
@ -100,9 +100,9 @@ fn main() -> Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn file() -> Result<TreeArc<SourceFile>> {
|
fn file() -> Result<SourceFile> {
|
||||||
let text = read_stdin()?;
|
let text = read_stdin()?;
|
||||||
Ok(SourceFile::parse(&text).tree().to_owned())
|
Ok(SourceFile::parse(&text).tree())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_stdin() -> Result<String> {
|
fn read_stdin() -> Result<String> {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, AstNode, AstToken},
|
ast::{self, AstNode, AstToken},
|
||||||
SyntaxKind,
|
SmolStr, SyntaxKind,
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
SyntaxNode, SyntaxToken, T,
|
SyntaxNode, SyntaxToken, T,
|
||||||
};
|
};
|
||||||
@ -15,12 +15,12 @@ pub fn reindent(text: &str, indent: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// If the node is on the beginning of the line, calculate indent.
|
/// If the node is on the beginning of the line, calculate indent.
|
||||||
pub fn leading_indent(node: &SyntaxNode) -> Option<&str> {
|
pub fn leading_indent(node: &SyntaxNode) -> Option<SmolStr> {
|
||||||
for token in prev_tokens(node.first_token()?) {
|
for token in prev_tokens(node.first_token()?) {
|
||||||
if let Some(ws) = ast::Whitespace::cast(token) {
|
if let Some(ws) = ast::Whitespace::cast(token.clone()) {
|
||||||
let ws_text = ws.text();
|
let ws_text = ws.text();
|
||||||
if let Some(pos) = ws_text.rfind('\n') {
|
if let Some(pos) = ws_text.rfind('\n') {
|
||||||
return Some(&ws_text[pos + 1..]);
|
return Some(ws_text[pos + 1..].into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if token.text().contains('\n') {
|
if token.text().contains('\n') {
|
||||||
@ -31,17 +31,17 @@ pub fn leading_indent(node: &SyntaxNode) -> Option<&str> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
|
fn prev_tokens(token: SyntaxToken) -> impl Iterator<Item = SyntaxToken> {
|
||||||
successors(token.prev_token(), |&token| token.prev_token())
|
successors(token.prev_token(), |token| token.prev_token())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_trivial_expression(block: &ast::Block) -> Option<&ast::Expr> {
|
pub fn extract_trivial_expression(block: &ast::Block) -> Option<ast::Expr> {
|
||||||
let expr = block.expr()?;
|
let expr = block.expr()?;
|
||||||
if expr.syntax().text().contains('\n') {
|
if expr.syntax().text().contains('\n') {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let non_trivial_children = block.syntax().children().filter(|it| match it.kind() {
|
let non_trivial_children = block.syntax().children().filter(|it| match it.kind() {
|
||||||
WHITESPACE | T!['{'] | T!['}'] => false,
|
WHITESPACE | T!['{'] | T!['}'] => false,
|
||||||
_ => it != &expr.syntax(),
|
_ => it != expr.syntax(),
|
||||||
});
|
});
|
||||||
if non_trivial_children.count() > 0 {
|
if non_trivial_children.count() > 0 {
|
||||||
return None;
|
return None;
|
||||||
|
@ -4,10 +4,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ra_arena::{impl_arena_id, Arena, RawId};
|
use ra_arena::{impl_arena_id, Arena, RawId};
|
||||||
use ra_syntax::{
|
use ra_syntax::ast::{self, NameOwner, StructKind, TypeAscriptionOwner};
|
||||||
ast::{self, NameOwner, StructKind, TypeAscriptionOwner},
|
|
||||||
TreeArc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
type_ref::TypeRef, AsName, AstDatabase, Crate, DefDatabase, Enum, EnumVariant, FieldSource,
|
type_ref::TypeRef, AsName, AstDatabase, Crate, DefDatabase, Enum, EnumVariant, FieldSource,
|
||||||
@ -59,11 +56,11 @@ impl StructData {
|
|||||||
struct_: Struct,
|
struct_: Struct,
|
||||||
) -> Arc<StructData> {
|
) -> Arc<StructData> {
|
||||||
let src = struct_.source(db);
|
let src = struct_.source(db);
|
||||||
Arc::new(StructData::new(&*src.ast))
|
Arc::new(StructData::new(&src.ast))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variants(enum_def: &ast::EnumDef) -> impl Iterator<Item = &ast::EnumVariant> {
|
fn variants(enum_def: &ast::EnumDef) -> impl Iterator<Item = ast::EnumVariant> {
|
||||||
enum_def.variant_list().into_iter().flat_map(|it| it.variants())
|
enum_def.variant_list().into_iter().flat_map(|it| it.variants())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,9 +68,9 @@ impl EnumVariant {
|
|||||||
pub(crate) fn source_impl(
|
pub(crate) fn source_impl(
|
||||||
self,
|
self,
|
||||||
db: &(impl DefDatabase + AstDatabase),
|
db: &(impl DefDatabase + AstDatabase),
|
||||||
) -> Source<TreeArc<ast::EnumVariant>> {
|
) -> Source<ast::EnumVariant> {
|
||||||
let src = self.parent.source(db);
|
let src = self.parent.source(db);
|
||||||
let ast = variants(&*src.ast)
|
let ast = variants(&src.ast)
|
||||||
.zip(db.enum_data(self.parent).variants.iter())
|
.zip(db.enum_data(self.parent).variants.iter())
|
||||||
.find(|(_syntax, (id, _))| *id == self.id)
|
.find(|(_syntax, (id, _))| *id == self.id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -96,7 +93,7 @@ impl EnumData {
|
|||||||
pub(crate) fn enum_data_query(db: &(impl DefDatabase + AstDatabase), e: Enum) -> Arc<EnumData> {
|
pub(crate) fn enum_data_query(db: &(impl DefDatabase + AstDatabase), e: Enum) -> Arc<EnumData> {
|
||||||
let src = e.source(db);
|
let src = e.source(db);
|
||||||
let name = src.ast.name().map(|n| n.as_name());
|
let name = src.ast.name().map(|n| n.as_name());
|
||||||
let variants = variants(&*src.ast)
|
let variants = variants(&src.ast)
|
||||||
.map(|var| EnumVariantData {
|
.map(|var| EnumVariantData {
|
||||||
name: var.name().map(|it| it.as_name()),
|
name: var.name().map(|it| it.as_name()),
|
||||||
variant_data: Arc::new(VariantData::new(var.kind())),
|
variant_data: Arc::new(VariantData::new(var.kind())),
|
||||||
|
@ -4,10 +4,7 @@ pub(crate) mod docs;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ra_db::{CrateId, Edition, FileId, SourceRootId};
|
use ra_db::{CrateId, Edition, FileId, SourceRootId};
|
||||||
use ra_syntax::{
|
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
|
||||||
ast::{self, NameOwner, TypeAscriptionOwner},
|
|
||||||
TreeArc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
adt::{EnumVariantId, StructFieldId, VariantDef},
|
adt::{EnumVariantId, StructFieldId, VariantDef},
|
||||||
@ -155,8 +152,8 @@ impl_froms!(
|
|||||||
);
|
);
|
||||||
|
|
||||||
pub enum ModuleSource {
|
pub enum ModuleSource {
|
||||||
SourceFile(TreeArc<ast::SourceFile>),
|
SourceFile(ast::SourceFile),
|
||||||
Module(TreeArc<ast::Module>),
|
Module(ast::Module),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleSource {
|
impl ModuleSource {
|
||||||
@ -199,7 +196,7 @@ impl Module {
|
|||||||
self,
|
self,
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
import: ImportId,
|
import: ImportId,
|
||||||
) -> Either<TreeArc<ast::UseTree>, TreeArc<ast::ExternCrateItem>> {
|
) -> Either<ast::UseTree, ast::ExternCrateItem> {
|
||||||
let src = self.definition_source(db);
|
let src = self.definition_source(db);
|
||||||
let (_, source_map) = db.raw_items_with_source_map(src.file_id);
|
let (_, source_map) = db.raw_items_with_source_map(src.file_id);
|
||||||
source_map.get(&src.ast, import)
|
source_map.get(&src.ast, import)
|
||||||
@ -321,8 +318,8 @@ pub struct StructField {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FieldSource {
|
pub enum FieldSource {
|
||||||
Named(TreeArc<ast::NamedFieldDef>),
|
Named(ast::NamedFieldDef),
|
||||||
Pos(TreeArc<ast::PosFieldDef>),
|
Pos(ast::PosFieldDef),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructField {
|
impl StructField {
|
||||||
@ -736,7 +733,7 @@ impl ConstData {
|
|||||||
konst: Const,
|
konst: Const,
|
||||||
) -> Arc<ConstData> {
|
) -> Arc<ConstData> {
|
||||||
let node = konst.source(db).ast;
|
let node = konst.source(db).ast;
|
||||||
const_data_for(&*node)
|
const_data_for(&node)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn static_data_query(
|
pub(crate) fn static_data_query(
|
||||||
@ -744,7 +741,7 @@ impl ConstData {
|
|||||||
konst: Static,
|
konst: Static,
|
||||||
) -> Arc<ConstData> {
|
) -> Arc<ConstData> {
|
||||||
let node = konst.source(db).ast;
|
let node = konst.source(db).ast;
|
||||||
const_data_for(&*node)
|
const_data_for(&node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,21 +71,21 @@ pub(crate) fn documentation_query(
|
|||||||
def: DocDef,
|
def: DocDef,
|
||||||
) -> Option<Documentation> {
|
) -> Option<Documentation> {
|
||||||
match def {
|
match def {
|
||||||
DocDef::Module(it) => docs_from_ast(&*it.declaration_source(db)?.ast),
|
DocDef::Module(it) => docs_from_ast(&it.declaration_source(db)?.ast),
|
||||||
DocDef::StructField(it) => match it.source(db).ast {
|
DocDef::StructField(it) => match it.source(db).ast {
|
||||||
FieldSource::Named(named) => docs_from_ast(&*named),
|
FieldSource::Named(named) => docs_from_ast(&named),
|
||||||
FieldSource::Pos(..) => None,
|
FieldSource::Pos(..) => None,
|
||||||
},
|
},
|
||||||
DocDef::Struct(it) => docs_from_ast(&*it.source(db).ast),
|
DocDef::Struct(it) => docs_from_ast(&it.source(db).ast),
|
||||||
DocDef::Enum(it) => docs_from_ast(&*it.source(db).ast),
|
DocDef::Enum(it) => docs_from_ast(&it.source(db).ast),
|
||||||
DocDef::EnumVariant(it) => docs_from_ast(&*it.source(db).ast),
|
DocDef::EnumVariant(it) => docs_from_ast(&it.source(db).ast),
|
||||||
DocDef::Static(it) => docs_from_ast(&*it.source(db).ast),
|
DocDef::Static(it) => docs_from_ast(&it.source(db).ast),
|
||||||
DocDef::Const(it) => docs_from_ast(&*it.source(db).ast),
|
DocDef::Const(it) => docs_from_ast(&it.source(db).ast),
|
||||||
DocDef::Function(it) => docs_from_ast(&*it.source(db).ast),
|
DocDef::Function(it) => docs_from_ast(&it.source(db).ast),
|
||||||
DocDef::Union(it) => docs_from_ast(&*it.source(db).ast),
|
DocDef::Union(it) => docs_from_ast(&it.source(db).ast),
|
||||||
DocDef::Trait(it) => docs_from_ast(&*it.source(db).ast),
|
DocDef::Trait(it) => docs_from_ast(&it.source(db).ast),
|
||||||
DocDef::TypeAlias(it) => docs_from_ast(&*it.source(db).ast),
|
DocDef::TypeAlias(it) => docs_from_ast(&it.source(db).ast),
|
||||||
DocDef::MacroDef(it) => docs_from_ast(&*it.source(db).ast),
|
DocDef::MacroDef(it) => docs_from_ast(&it.source(db).ast),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use ra_syntax::{ast, TreeArc};
|
use ra_syntax::ast;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ids::AstItemDef, AstDatabase, Const, DefDatabase, Enum, EnumVariant, FieldSource, Function,
|
ids::AstItemDef, AstDatabase, Const, DefDatabase, Enum, EnumVariant, FieldSource, Function,
|
||||||
@ -34,7 +34,7 @@ impl Module {
|
|||||||
pub fn declaration_source(
|
pub fn declaration_source(
|
||||||
self,
|
self,
|
||||||
db: &(impl DefDatabase + AstDatabase),
|
db: &(impl DefDatabase + AstDatabase),
|
||||||
) -> Option<Source<TreeArc<ast::Module>>> {
|
) -> Option<Source<ast::Module>> {
|
||||||
let def_map = db.crate_def_map(self.krate);
|
let def_map = db.crate_def_map(self.krate);
|
||||||
let decl = def_map[self.module_id].declaration?;
|
let decl = def_map[self.module_id].declaration?;
|
||||||
let ast = decl.to_node(db);
|
let ast = decl.to_node(db);
|
||||||
@ -49,62 +49,62 @@ impl HasSource for StructField {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl HasSource for Struct {
|
impl HasSource for Struct {
|
||||||
type Ast = TreeArc<ast::StructDef>;
|
type Ast = ast::StructDef;
|
||||||
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<TreeArc<ast::StructDef>> {
|
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::StructDef> {
|
||||||
self.id.source(db)
|
self.id.source(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl HasSource for Union {
|
impl HasSource for Union {
|
||||||
type Ast = TreeArc<ast::StructDef>;
|
type Ast = ast::StructDef;
|
||||||
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<TreeArc<ast::StructDef>> {
|
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::StructDef> {
|
||||||
self.id.source(db)
|
self.id.source(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl HasSource for Enum {
|
impl HasSource for Enum {
|
||||||
type Ast = TreeArc<ast::EnumDef>;
|
type Ast = ast::EnumDef;
|
||||||
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<TreeArc<ast::EnumDef>> {
|
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::EnumDef> {
|
||||||
self.id.source(db)
|
self.id.source(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl HasSource for EnumVariant {
|
impl HasSource for EnumVariant {
|
||||||
type Ast = TreeArc<ast::EnumVariant>;
|
type Ast = ast::EnumVariant;
|
||||||
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<TreeArc<ast::EnumVariant>> {
|
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::EnumVariant> {
|
||||||
self.source_impl(db)
|
self.source_impl(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl HasSource for Function {
|
impl HasSource for Function {
|
||||||
type Ast = TreeArc<ast::FnDef>;
|
type Ast = ast::FnDef;
|
||||||
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<TreeArc<ast::FnDef>> {
|
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::FnDef> {
|
||||||
self.id.source(db)
|
self.id.source(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl HasSource for Const {
|
impl HasSource for Const {
|
||||||
type Ast = TreeArc<ast::ConstDef>;
|
type Ast = ast::ConstDef;
|
||||||
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<TreeArc<ast::ConstDef>> {
|
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::ConstDef> {
|
||||||
self.id.source(db)
|
self.id.source(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl HasSource for Static {
|
impl HasSource for Static {
|
||||||
type Ast = TreeArc<ast::StaticDef>;
|
type Ast = ast::StaticDef;
|
||||||
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<TreeArc<ast::StaticDef>> {
|
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::StaticDef> {
|
||||||
self.id.source(db)
|
self.id.source(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl HasSource for Trait {
|
impl HasSource for Trait {
|
||||||
type Ast = TreeArc<ast::TraitDef>;
|
type Ast = ast::TraitDef;
|
||||||
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<TreeArc<ast::TraitDef>> {
|
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::TraitDef> {
|
||||||
self.id.source(db)
|
self.id.source(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl HasSource for TypeAlias {
|
impl HasSource for TypeAlias {
|
||||||
type Ast = TreeArc<ast::TypeAliasDef>;
|
type Ast = ast::TypeAliasDef;
|
||||||
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<TreeArc<ast::TypeAliasDef>> {
|
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::TypeAliasDef> {
|
||||||
self.id.source(db)
|
self.id.source(db)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl HasSource for MacroDef {
|
impl HasSource for MacroDef {
|
||||||
type Ast = TreeArc<ast::MacroCall>;
|
type Ast = ast::MacroCall;
|
||||||
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<TreeArc<ast::MacroCall>> {
|
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::MacroCall> {
|
||||||
Source { file_id: self.id.0.file_id(), ast: self.id.0.to_node(db) }
|
Source { file_id: self.id.0.file_id(), ast: self.id.0.to_node(db) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use ra_db::{salsa, SourceDatabase};
|
use ra_db::{salsa, SourceDatabase};
|
||||||
use ra_syntax::{ast, Parse, SmolStr, SyntaxNode, TreeArc};
|
use ra_syntax::{ast, Parse, SmolStr, SyntaxNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
adt::{EnumData, StructData},
|
adt::{EnumData, StructData},
|
||||||
@ -62,11 +62,11 @@ pub trait AstDatabase: InternDatabase {
|
|||||||
|
|
||||||
#[salsa::transparent]
|
#[salsa::transparent]
|
||||||
#[salsa::invoke(crate::source_id::AstIdMap::file_item_query)]
|
#[salsa::invoke(crate::source_id::AstIdMap::file_item_query)]
|
||||||
fn ast_id_to_node(&self, file_id: HirFileId, ast_id: ErasedFileAstId) -> TreeArc<SyntaxNode>;
|
fn ast_id_to_node(&self, file_id: HirFileId, ast_id: ErasedFileAstId) -> SyntaxNode;
|
||||||
|
|
||||||
#[salsa::transparent]
|
#[salsa::transparent]
|
||||||
#[salsa::invoke(crate::ids::HirFileId::parse_or_expand_query)]
|
#[salsa::invoke(crate::ids::HirFileId::parse_or_expand_query)]
|
||||||
fn parse_or_expand(&self, file_id: HirFileId) -> Option<TreeArc<SyntaxNode>>;
|
fn parse_or_expand(&self, file_id: HirFileId) -> Option<SyntaxNode>;
|
||||||
|
|
||||||
#[salsa::invoke(crate::ids::HirFileId::parse_macro_query)]
|
#[salsa::invoke(crate::ids::HirFileId::parse_macro_query)]
|
||||||
fn parse_macro(&self, macro_file: ids::MacroFile) -> Option<Parse<SyntaxNode>>;
|
fn parse_macro(&self, macro_file: ids::MacroFile) -> Option<Parse<SyntaxNode>>;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::{any::Any, fmt};
|
use std::{any::Any, fmt};
|
||||||
|
|
||||||
use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, TextRange, TreeArc};
|
use ra_syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, TextRange};
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
|
|
||||||
use crate::{HirDatabase, HirFileId, Name};
|
use crate::{HirDatabase, HirFileId, Name};
|
||||||
@ -33,9 +33,9 @@ pub trait AstDiagnostic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl dyn Diagnostic {
|
impl dyn Diagnostic {
|
||||||
pub fn syntax_node(&self, db: &impl HirDatabase) -> TreeArc<SyntaxNode> {
|
pub fn syntax_node(&self, db: &impl HirDatabase) -> SyntaxNode {
|
||||||
let node = db.parse_or_expand(self.file()).unwrap();
|
let node = db.parse_or_expand(self.file()).unwrap();
|
||||||
self.syntax_node_ptr().to_node(&*node).to_owned()
|
self.syntax_node_ptr().to_node(&node)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
|
pub fn downcast_ref<D: Diagnostic>(&self) -> Option<&D> {
|
||||||
@ -143,11 +143,11 @@ impl Diagnostic for MissingFields {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AstDiagnostic for MissingFields {
|
impl AstDiagnostic for MissingFields {
|
||||||
type AST = TreeArc<ast::NamedFieldList>;
|
type AST = ast::NamedFieldList;
|
||||||
|
|
||||||
fn ast(&self, db: &impl HirDatabase) -> Self::AST {
|
fn ast(&self, db: &impl HirDatabase) -> Self::AST {
|
||||||
let root = db.parse_or_expand(self.file()).unwrap();
|
let root = db.parse_or_expand(self.file()).unwrap();
|
||||||
let node = self.syntax_node_ptr().to_node(&*root);
|
let node = self.syntax_node_ptr().to_node(&root);
|
||||||
ast::NamedFieldList::cast(&node).unwrap().to_owned()
|
ast::NamedFieldList::cast(node).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -550,7 +550,7 @@ where
|
|||||||
self.exprs.alloc(block)
|
self.exprs.alloc(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_expr(&mut self, expr: &ast::Expr) -> ExprId {
|
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
|
||||||
let syntax_ptr = SyntaxNodePtr::new(expr.syntax());
|
let syntax_ptr = SyntaxNodePtr::new(expr.syntax());
|
||||||
match expr.kind() {
|
match expr.kind() {
|
||||||
ast::ExprKind::IfExpr(e) => {
|
ast::ExprKind::IfExpr(e) => {
|
||||||
@ -565,7 +565,8 @@ where
|
|||||||
.map(|b| match b {
|
.map(|b| match b {
|
||||||
ast::ElseBranch::Block(it) => self.collect_block(it),
|
ast::ElseBranch::Block(it) => self.collect_block(it),
|
||||||
ast::ElseBranch::IfExpr(elif) => {
|
ast::ElseBranch::IfExpr(elif) => {
|
||||||
let expr: &ast::Expr = ast::Expr::cast(elif.syntax()).unwrap();
|
let expr: ast::Expr =
|
||||||
|
ast::Expr::cast(elif.syntax().clone()).unwrap();
|
||||||
self.collect_expr(expr)
|
self.collect_expr(expr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -582,7 +583,7 @@ where
|
|||||||
let else_branch = e.else_branch().map(|b| match b {
|
let else_branch = e.else_branch().map(|b| match b {
|
||||||
ast::ElseBranch::Block(it) => self.collect_block(it),
|
ast::ElseBranch::Block(it) => self.collect_block(it),
|
||||||
ast::ElseBranch::IfExpr(elif) => {
|
ast::ElseBranch::IfExpr(elif) => {
|
||||||
let expr: &ast::Expr = ast::Expr::cast(elif.syntax()).unwrap();
|
let expr: ast::Expr = ast::Expr::cast(elif.syntax().clone()).unwrap();
|
||||||
self.collect_expr(expr)
|
self.collect_expr(expr)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -689,7 +690,7 @@ where
|
|||||||
let struct_lit = if let Some(nfl) = e.named_field_list() {
|
let struct_lit = if let Some(nfl) = e.named_field_list() {
|
||||||
let fields = nfl
|
let fields = nfl
|
||||||
.fields()
|
.fields()
|
||||||
.inspect(|field| field_ptrs.push(AstPtr::new(*field)))
|
.inspect(|field| field_ptrs.push(AstPtr::new(field)))
|
||||||
.map(|field| StructLitField {
|
.map(|field| StructLitField {
|
||||||
name: field
|
name: field
|
||||||
.name_ref()
|
.name_ref()
|
||||||
@ -699,7 +700,7 @@ where
|
|||||||
self.collect_expr(e)
|
self.collect_expr(e)
|
||||||
} else if let Some(nr) = field.name_ref() {
|
} else if let Some(nr) = field.name_ref() {
|
||||||
// field shorthand
|
// field shorthand
|
||||||
let id = self.exprs.alloc(Expr::Path(Path::from_name_ref(nr)));
|
let id = self.exprs.alloc(Expr::Path(Path::from_name_ref(&nr)));
|
||||||
self.source_map
|
self.source_map
|
||||||
.expr_map
|
.expr_map
|
||||||
.insert(SyntaxNodePtr::new(nr.syntax()), id);
|
.insert(SyntaxNodePtr::new(nr.syntax()), id);
|
||||||
@ -837,7 +838,7 @@ where
|
|||||||
let ast_id = self
|
let ast_id = self
|
||||||
.db
|
.db
|
||||||
.ast_id_map(self.current_file_id)
|
.ast_id_map(self.current_file_id)
|
||||||
.ast_id(e)
|
.ast_id(&e)
|
||||||
.with_file_id(self.current_file_id);
|
.with_file_id(self.current_file_id);
|
||||||
|
|
||||||
if let Some(path) = e.path().and_then(Path::from_ast) {
|
if let Some(path) = e.path().and_then(Path::from_ast) {
|
||||||
@ -845,11 +846,11 @@ where
|
|||||||
let call_id = MacroCallLoc { def: def.id, ast_id }.id(self.db);
|
let call_id = MacroCallLoc { def: def.id, ast_id }.id(self.db);
|
||||||
let file_id = call_id.as_file(MacroFileKind::Expr);
|
let file_id = call_id.as_file(MacroFileKind::Expr);
|
||||||
if let Some(node) = self.db.parse_or_expand(file_id) {
|
if let Some(node) = self.db.parse_or_expand(file_id) {
|
||||||
if let Some(expr) = ast::Expr::cast(&*node) {
|
if let Some(expr) = ast::Expr::cast(node) {
|
||||||
log::debug!("macro expansion {}", expr.syntax().debug_dump());
|
log::debug!("macro expansion {}", expr.syntax().debug_dump());
|
||||||
let old_file_id =
|
let old_file_id =
|
||||||
std::mem::replace(&mut self.current_file_id, file_id);
|
std::mem::replace(&mut self.current_file_id, file_id);
|
||||||
let id = self.collect_expr(&expr);
|
let id = self.collect_expr(expr);
|
||||||
self.current_file_id = old_file_id;
|
self.current_file_id = old_file_id;
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@ -863,7 +864,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_expr_opt(&mut self, expr: Option<&ast::Expr>) -> ExprId {
|
fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
|
||||||
if let Some(expr) = expr {
|
if let Some(expr) = expr {
|
||||||
self.collect_expr(expr)
|
self.collect_expr(expr)
|
||||||
} else {
|
} else {
|
||||||
@ -871,7 +872,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_block(&mut self, block: &ast::Block) -> ExprId {
|
fn collect_block(&mut self, block: ast::Block) -> ExprId {
|
||||||
let statements = block
|
let statements = block
|
||||||
.statements()
|
.statements()
|
||||||
.map(|s| match s.kind() {
|
.map(|s| match s.kind() {
|
||||||
@ -890,7 +891,7 @@ where
|
|||||||
self.alloc_expr(Expr::Block { statements, tail }, SyntaxNodePtr::new(block.syntax()))
|
self.alloc_expr(Expr::Block { statements, tail }, SyntaxNodePtr::new(block.syntax()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_block_opt(&mut self, block: Option<&ast::Block>) -> ExprId {
|
fn collect_block_opt(&mut self, block: Option<ast::Block>) -> ExprId {
|
||||||
if let Some(block) = block {
|
if let Some(block) = block {
|
||||||
self.collect_block(block)
|
self.collect_block(block)
|
||||||
} else {
|
} else {
|
||||||
@ -898,7 +899,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_pat(&mut self, pat: &ast::Pat) -> PatId {
|
fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
|
||||||
let pattern = match pat.kind() {
|
let pattern = match pat.kind() {
|
||||||
ast::PatKind::BindPat(bp) => {
|
ast::PatKind::BindPat(bp) => {
|
||||||
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
|
let name = bp.name().map(|nr| nr.as_name()).unwrap_or_else(Name::missing);
|
||||||
@ -932,7 +933,8 @@ where
|
|||||||
let mut fields: Vec<_> = field_pat_list
|
let mut fields: Vec<_> = field_pat_list
|
||||||
.bind_pats()
|
.bind_pats()
|
||||||
.filter_map(|bind_pat| {
|
.filter_map(|bind_pat| {
|
||||||
let ast_pat = ast::Pat::cast(bind_pat.syntax()).expect("bind pat is a pat");
|
let ast_pat =
|
||||||
|
ast::Pat::cast(bind_pat.syntax().clone()).expect("bind pat is a pat");
|
||||||
let pat = self.collect_pat(ast_pat);
|
let pat = self.collect_pat(ast_pat);
|
||||||
let name = bind_pat.name()?.as_name();
|
let name = bind_pat.name()?.as_name();
|
||||||
Some(FieldPat { name, pat })
|
Some(FieldPat { name, pat })
|
||||||
@ -953,11 +955,11 @@ where
|
|||||||
ast::PatKind::LiteralPat(_) => Pat::Missing,
|
ast::PatKind::LiteralPat(_) => Pat::Missing,
|
||||||
ast::PatKind::SlicePat(_) | ast::PatKind::RangePat(_) => Pat::Missing,
|
ast::PatKind::SlicePat(_) | ast::PatKind::RangePat(_) => Pat::Missing,
|
||||||
};
|
};
|
||||||
let ptr = AstPtr::new(pat);
|
let ptr = AstPtr::new(&pat);
|
||||||
self.alloc_pat(pattern, Either::A(ptr))
|
self.alloc_pat(pattern, Either::A(ptr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_pat_opt(&mut self, pat: Option<&ast::Pat>) -> PatId {
|
fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
|
||||||
if let Some(pat) = pat {
|
if let Some(pat) = pat {
|
||||||
self.collect_pat(pat)
|
self.collect_pat(pat)
|
||||||
} else {
|
} else {
|
||||||
@ -965,20 +967,20 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_const_body(&mut self, node: &ast::ConstDef) {
|
fn collect_const_body(&mut self, node: ast::ConstDef) {
|
||||||
let body = self.collect_expr_opt(node.body());
|
let body = self.collect_expr_opt(node.body());
|
||||||
self.body_expr = Some(body);
|
self.body_expr = Some(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_static_body(&mut self, node: &ast::StaticDef) {
|
fn collect_static_body(&mut self, node: ast::StaticDef) {
|
||||||
let body = self.collect_expr_opt(node.body());
|
let body = self.collect_expr_opt(node.body());
|
||||||
self.body_expr = Some(body);
|
self.body_expr = Some(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_fn_body(&mut self, node: &ast::FnDef) {
|
fn collect_fn_body(&mut self, node: ast::FnDef) {
|
||||||
if let Some(param_list) = node.param_list() {
|
if let Some(param_list) = node.param_list() {
|
||||||
if let Some(self_param) = param_list.self_param() {
|
if let Some(self_param) = param_list.self_param() {
|
||||||
let ptr = AstPtr::new(self_param);
|
let ptr = AstPtr::new(&self_param);
|
||||||
let param_pat = self.alloc_pat(
|
let param_pat = self.alloc_pat(
|
||||||
Pat::Bind {
|
Pat::Bind {
|
||||||
name: SELF_PARAM,
|
name: SELF_PARAM,
|
||||||
@ -1027,17 +1029,17 @@ pub(crate) fn body_with_source_map_query(
|
|||||||
DefWithBody::Const(ref c) => {
|
DefWithBody::Const(ref c) => {
|
||||||
let src = c.source(db);
|
let src = c.source(db);
|
||||||
collector = ExprCollector::new(def, src.file_id, def.resolver(db), db);
|
collector = ExprCollector::new(def, src.file_id, def.resolver(db), db);
|
||||||
collector.collect_const_body(&src.ast)
|
collector.collect_const_body(src.ast)
|
||||||
}
|
}
|
||||||
DefWithBody::Function(ref f) => {
|
DefWithBody::Function(ref f) => {
|
||||||
let src = f.source(db);
|
let src = f.source(db);
|
||||||
collector = ExprCollector::new(def, src.file_id, def.resolver(db), db);
|
collector = ExprCollector::new(def, src.file_id, def.resolver(db), db);
|
||||||
collector.collect_fn_body(&src.ast)
|
collector.collect_fn_body(src.ast)
|
||||||
}
|
}
|
||||||
DefWithBody::Static(ref s) => {
|
DefWithBody::Static(ref s) => {
|
||||||
let src = s.source(db);
|
let src = s.source(db);
|
||||||
collector = ExprCollector::new(def, src.file_id, def.resolver(db), db);
|
collector = ExprCollector::new(def, src.file_id, def.resolver(db), db);
|
||||||
collector.collect_static_body(&src.ast)
|
collector.collect_static_body(src.ast)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +190,7 @@ mod tests {
|
|||||||
|
|
||||||
let (db, _source_root, file_id) = MockDatabase::with_single_file(&code);
|
let (db, _source_root, file_id) = MockDatabase::with_single_file(&code);
|
||||||
let file = db.parse(file_id).ok().unwrap();
|
let file = db.parse(file_id).ok().unwrap();
|
||||||
let marker: &ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
|
let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
|
||||||
let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None);
|
let analyzer = SourceAnalyzer::new(&db, file_id, marker.syntax(), None);
|
||||||
|
|
||||||
let scopes = analyzer.scopes();
|
let scopes = analyzer.scopes();
|
||||||
@ -290,10 +290,10 @@ mod tests {
|
|||||||
let file = db.parse(file_id).ok().unwrap();
|
let file = db.parse(file_id).ok().unwrap();
|
||||||
let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
|
let expected_name = find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into())
|
||||||
.expect("failed to find a name at the target offset");
|
.expect("failed to find a name at the target offset");
|
||||||
let name_ref: &ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
|
let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
|
||||||
let analyzer = SourceAnalyzer::new(&db, file_id, name_ref.syntax(), None);
|
let analyzer = SourceAnalyzer::new(&db, file_id, name_ref.syntax(), None);
|
||||||
|
|
||||||
let local_name_entry = analyzer.resolve_local_name(name_ref).unwrap();
|
let local_name_entry = analyzer.resolve_local_name(&name_ref).unwrap();
|
||||||
let local_name =
|
let local_name =
|
||||||
local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
|
local_name_entry.ptr().either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
|
||||||
assert_eq!(local_name.range(), expected_name.syntax().range());
|
assert_eq!(local_name.range(), expected_name.syntax().range());
|
||||||
|
@ -79,7 +79,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
|
|||||||
.and_then(StructLit::cast)
|
.and_then(StructLit::cast)
|
||||||
.and_then(|lit| lit.named_field_list())
|
.and_then(|lit| lit.named_field_list())
|
||||||
{
|
{
|
||||||
let field_list_ptr = AstPtr::new(field_list_node);
|
let field_list_ptr = AstPtr::new(&field_list_node);
|
||||||
self.sink.push(MissingFields {
|
self.sink.push(MissingFields {
|
||||||
file: file_id,
|
file: file_id,
|
||||||
field_list: field_list_ptr,
|
field_list: field_list_ptr,
|
||||||
|
@ -76,17 +76,17 @@ impl GenericParams {
|
|||||||
generics.parent_params = parent.map(|p| db.generic_params(p));
|
generics.parent_params = parent.map(|p| db.generic_params(p));
|
||||||
let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32;
|
let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32;
|
||||||
match def {
|
match def {
|
||||||
GenericDef::Function(it) => generics.fill(&*it.source(db).ast, start),
|
GenericDef::Function(it) => generics.fill(&it.source(db).ast, start),
|
||||||
GenericDef::Struct(it) => generics.fill(&*it.source(db).ast, start),
|
GenericDef::Struct(it) => generics.fill(&it.source(db).ast, start),
|
||||||
GenericDef::Union(it) => generics.fill(&*it.source(db).ast, start),
|
GenericDef::Union(it) => generics.fill(&it.source(db).ast, start),
|
||||||
GenericDef::Enum(it) => generics.fill(&*it.source(db).ast, start),
|
GenericDef::Enum(it) => generics.fill(&it.source(db).ast, start),
|
||||||
GenericDef::Trait(it) => {
|
GenericDef::Trait(it) => {
|
||||||
// traits get the Self type as an implicit first type parameter
|
// traits get the Self type as an implicit first type parameter
|
||||||
generics.params.push(GenericParam { idx: start, name: SELF_TYPE, default: None });
|
generics.params.push(GenericParam { idx: start, name: SELF_TYPE, default: None });
|
||||||
generics.fill(&*it.source(db).ast, start + 1);
|
generics.fill(&it.source(db).ast, start + 1);
|
||||||
}
|
}
|
||||||
GenericDef::TypeAlias(it) => generics.fill(&*it.source(db).ast, start),
|
GenericDef::TypeAlias(it) => generics.fill(&it.source(db).ast, start),
|
||||||
GenericDef::ImplBlock(it) => generics.fill(&*it.source(db).ast, start),
|
GenericDef::ImplBlock(it) => generics.fill(&it.source(db).ast, start),
|
||||||
GenericDef::EnumVariant(_) => {}
|
GenericDef::EnumVariant(_) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,9 +102,9 @@ impl GenericParams {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_params(&mut self, params: &ast::TypeParamList, start: u32) {
|
fn fill_params(&mut self, params: ast::TypeParamList, start: u32) {
|
||||||
for (idx, type_param) in params.type_params().enumerate() {
|
for (idx, type_param) in params.type_params().enumerate() {
|
||||||
let name = type_param.name().map(AsName::as_name).unwrap_or_else(Name::missing);
|
let name = type_param.name().map_or_else(Name::missing, |it| it.as_name());
|
||||||
let default = type_param.default_type().and_then(|t| t.path()).and_then(Path::from_ast);
|
let default = type_param.default_type().and_then(|t| t.path()).and_then(Path::from_ast);
|
||||||
|
|
||||||
let param = GenericParam { idx: idx as u32 + start, name: name.clone(), default };
|
let param = GenericParam { idx: idx as u32 + start, name: name.clone(), default };
|
||||||
@ -121,7 +121,7 @@ impl GenericParams {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill_where_predicates(&mut self, where_clause: &ast::WhereClause) {
|
fn fill_where_predicates(&mut self, where_clause: ast::WhereClause) {
|
||||||
for pred in where_clause.predicates() {
|
for pred in where_clause.predicates() {
|
||||||
let type_ref = match pred.type_ref() {
|
let type_ref = match pred.type_ref() {
|
||||||
Some(type_ref) => type_ref,
|
Some(type_ref) => type_ref,
|
||||||
@ -134,7 +134,7 @@ impl GenericParams {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_where_predicate_from_bound(&mut self, bound: &ast::TypeBound, type_ref: TypeRef) {
|
fn add_where_predicate_from_bound(&mut self, bound: ast::TypeBound, type_ref: TypeRef) {
|
||||||
let path = bound
|
let path = bound
|
||||||
.type_ref()
|
.type_ref()
|
||||||
.and_then(|tr| match tr.kind() {
|
.and_then(|tr| match tr.kind() {
|
||||||
|
@ -6,7 +6,7 @@ use std::{
|
|||||||
use mbe::MacroRules;
|
use mbe::MacroRules;
|
||||||
use ra_db::{salsa, FileId};
|
use ra_db::{salsa, FileId};
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_syntax::{ast, AstNode, Parse, SyntaxNode, TreeArc};
|
use ra_syntax::{ast, AstNode, Parse, SyntaxNode};
|
||||||
|
|
||||||
use crate::{AstDatabase, AstId, DefDatabase, FileAstId, InternDatabase, Module, Source};
|
use crate::{AstDatabase, AstId, DefDatabase, FileAstId, InternDatabase, Module, Source};
|
||||||
|
|
||||||
@ -58,11 +58,11 @@ impl HirFileId {
|
|||||||
pub(crate) fn parse_or_expand_query(
|
pub(crate) fn parse_or_expand_query(
|
||||||
db: &impl AstDatabase,
|
db: &impl AstDatabase,
|
||||||
file_id: HirFileId,
|
file_id: HirFileId,
|
||||||
) -> Option<TreeArc<SyntaxNode>> {
|
) -> Option<SyntaxNode> {
|
||||||
match file_id.0 {
|
match file_id.0 {
|
||||||
HirFileIdRepr::File(file_id) => Some(db.parse(file_id).tree().syntax().to_owned()),
|
HirFileIdRepr::File(file_id) => Some(db.parse(file_id).tree().syntax().clone()),
|
||||||
HirFileIdRepr::Macro(macro_file) => {
|
HirFileIdRepr::Macro(macro_file) => {
|
||||||
db.parse_macro(macro_file).map(|it| it.tree().to_owned())
|
db.parse_macro(macro_file).map(|it| it.syntax_node())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,7 +123,7 @@ pub struct MacroDefId(pub(crate) AstId<ast::MacroCall>);
|
|||||||
pub(crate) fn macro_def_query(db: &impl AstDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> {
|
pub(crate) fn macro_def_query(db: &impl AstDatabase, id: MacroDefId) -> Option<Arc<MacroRules>> {
|
||||||
let macro_call = id.0.to_node(db);
|
let macro_call = id.0.to_node(db);
|
||||||
let arg = macro_call.token_tree()?;
|
let arg = macro_call.token_tree()?;
|
||||||
let (tt, _) = mbe::ast_to_token_tree(arg).or_else(|| {
|
let (tt, _) = mbe::ast_to_token_tree(&arg).or_else(|| {
|
||||||
log::warn!("fail on macro_def to token tree: {:#?}", arg);
|
log::warn!("fail on macro_def to token tree: {:#?}", arg);
|
||||||
None
|
None
|
||||||
})?;
|
})?;
|
||||||
@ -138,7 +138,7 @@ pub(crate) fn macro_arg_query(db: &impl AstDatabase, id: MacroCallId) -> Option<
|
|||||||
let loc = id.loc(db);
|
let loc = id.loc(db);
|
||||||
let macro_call = loc.ast_id.to_node(db);
|
let macro_call = loc.ast_id.to_node(db);
|
||||||
let arg = macro_call.token_tree()?;
|
let arg = macro_call.token_tree()?;
|
||||||
let (tt, _) = mbe::ast_to_token_tree(arg)?;
|
let (tt, _) = mbe::ast_to_token_tree(&arg)?;
|
||||||
Some(Arc::new(tt))
|
Some(Arc::new(tt))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ pub(crate) trait AstItemDef<N: AstNode>: salsa::InternKey + Clone {
|
|||||||
let loc = ItemLoc { module: ctx.module, ast_id: ast_id.with_file_id(ctx.file_id) };
|
let loc = ItemLoc { module: ctx.module, ast_id: ast_id.with_file_id(ctx.file_id) };
|
||||||
Self::intern(ctx.db, loc)
|
Self::intern(ctx.db, loc)
|
||||||
}
|
}
|
||||||
fn source(self, db: &(impl AstDatabase + DefDatabase)) -> Source<TreeArc<N>> {
|
fn source(self, db: &(impl AstDatabase + DefDatabase)) -> Source<N> {
|
||||||
let loc = self.lookup_intern(db);
|
let loc = self.lookup_intern(db);
|
||||||
let ast = loc.ast_id.to_node(db);
|
let ast = loc.ast_id.to_node(db);
|
||||||
Source { file_id: loc.ast_id.file_id(), ast }
|
Source { file_id: loc.ast_id.file_id(), ast }
|
||||||
|
@ -4,7 +4,7 @@ use std::sync::Arc;
|
|||||||
use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
|
use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
AstPtr, SourceFile, TreeArc,
|
AstPtr, SourceFile,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -28,9 +28,9 @@ impl ImplSourceMap {
|
|||||||
self.map.insert(impl_id, AstPtr::new(impl_block))
|
self.map.insert(impl_id, AstPtr::new(impl_block))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, source: &ModuleSource, impl_id: ImplId) -> TreeArc<ast::ImplBlock> {
|
pub fn get(&self, source: &ModuleSource, impl_id: ImplId) -> ast::ImplBlock {
|
||||||
let file = match source {
|
let file = match source {
|
||||||
ModuleSource::SourceFile(file) => &*file,
|
ModuleSource::SourceFile(file) => file.clone(),
|
||||||
ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
|
ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -45,8 +45,8 @@ pub struct ImplBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl HasSource for ImplBlock {
|
impl HasSource for ImplBlock {
|
||||||
type Ast = TreeArc<ast::ImplBlock>;
|
type Ast = ast::ImplBlock;
|
||||||
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<TreeArc<ast::ImplBlock>> {
|
fn source(self, db: &(impl DefDatabase + AstDatabase)) -> Source<ast::ImplBlock> {
|
||||||
let source_map = db.impls_in_module_with_source_map(self.module).1;
|
let source_map = db.impls_in_module_with_source_map(self.module).1;
|
||||||
let src = self.module.definition_source(db);
|
let src = self.module.definition_source(db);
|
||||||
Source { file_id: src.file_id, ast: source_map.get(&src.ast, self.impl_id) }
|
Source { file_id: src.file_id, ast: source_map.get(&src.ast, self.impl_id) }
|
||||||
@ -132,9 +132,9 @@ impl ImplData {
|
|||||||
item_list
|
item_list
|
||||||
.impl_items()
|
.impl_items()
|
||||||
.map(|item_node| match item_node.kind() {
|
.map(|item_node| match item_node.kind() {
|
||||||
ast::ImplItemKind::FnDef(it) => Function { id: ctx.to_def(it) }.into(),
|
ast::ImplItemKind::FnDef(it) => Function { id: ctx.to_def(&it) }.into(),
|
||||||
ast::ImplItemKind::ConstDef(it) => Const { id: ctx.to_def(it) }.into(),
|
ast::ImplItemKind::ConstDef(it) => Const { id: ctx.to_def(&it) }.into(),
|
||||||
ast::ImplItemKind::TypeAliasDef(it) => TypeAlias { id: ctx.to_def(it) }.into(),
|
ast::ImplItemKind::TypeAliasDef(it) => TypeAlias { id: ctx.to_def(&it) }.into(),
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
@ -202,20 +202,20 @@ impl ModuleImplBlocks {
|
|||||||
|
|
||||||
let src = m.module.definition_source(db);
|
let src = m.module.definition_source(db);
|
||||||
let node = match &src.ast {
|
let node = match &src.ast {
|
||||||
ModuleSource::SourceFile(node) => node.syntax(),
|
ModuleSource::SourceFile(node) => node.syntax().clone(),
|
||||||
ModuleSource::Module(node) => {
|
ModuleSource::Module(node) => {
|
||||||
node.item_list().expect("inline module should have item list").syntax()
|
node.item_list().expect("inline module should have item list").syntax().clone()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) {
|
for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) {
|
||||||
let impl_block = ImplData::from_ast(db, src.file_id, m.module, impl_block_ast);
|
let impl_block = ImplData::from_ast(db, src.file_id, m.module, &impl_block_ast);
|
||||||
let id = m.impls.alloc(impl_block);
|
let id = m.impls.alloc(impl_block);
|
||||||
for &impl_item in &m.impls[id].items {
|
for &impl_item in &m.impls[id].items {
|
||||||
m.impls_by_def.insert(impl_item, id);
|
m.impls_by_def.insert(impl_item, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
source_map.insert(id, impl_block_ast);
|
source_map.insert(id, &impl_block_ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
m
|
m
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use ra_syntax::{ast::AttrsOwner, SmolStr, TreeArc};
|
use ra_syntax::{ast::AttrsOwner, SmolStr};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AstDatabase, Crate, DefDatabase, Enum, Function, HasSource, HirDatabase, ImplBlock, Module,
|
AstDatabase, Crate, DefDatabase, Enum, Function, HasSource, HirDatabase, ImplBlock, Module,
|
||||||
@ -95,7 +95,7 @@ impl LangItems {
|
|||||||
// Look for impl targets
|
// Look for impl targets
|
||||||
for impl_block in module.impl_blocks(db) {
|
for impl_block in module.impl_blocks(db) {
|
||||||
let src = impl_block.source(db);
|
let src = impl_block.source(db);
|
||||||
if let Some(lang_item_name) = lang_item_name(&*src.ast) {
|
if let Some(lang_item_name) = lang_item_name(&src.ast) {
|
||||||
self.items
|
self.items
|
||||||
.entry(lang_item_name)
|
.entry(lang_item_name)
|
||||||
.or_insert_with(|| LangItemTarget::ImplBlock(impl_block));
|
.or_insert_with(|| LangItemTarget::ImplBlock(impl_block));
|
||||||
@ -137,11 +137,11 @@ impl LangItems {
|
|||||||
item: T,
|
item: T,
|
||||||
constructor: fn(T) -> LangItemTarget,
|
constructor: fn(T) -> LangItemTarget,
|
||||||
) where
|
) where
|
||||||
T: Copy + HasSource<Ast = TreeArc<N>>,
|
T: Copy + HasSource<Ast = N>,
|
||||||
N: AttrsOwner,
|
N: AttrsOwner,
|
||||||
{
|
{
|
||||||
let node = item.source(db).ast;
|
let node = item.source(db).ast;
|
||||||
if let Some(lang_item_name) = lang_item_name(&*node) {
|
if let Some(lang_item_name) = lang_item_name(&node) {
|
||||||
self.items.entry(lang_item_name).or_insert_with(|| constructor(item));
|
self.items.entry(lang_item_name).or_insert_with(|| constructor(item));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ impl AsName for ast::Name {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AsName for ast::FieldKind<'a> {
|
impl AsName for ast::FieldKind {
|
||||||
fn as_name(&self) -> Name {
|
fn as_name(&self) -> Name {
|
||||||
match self {
|
match self {
|
||||||
ast::FieldKind::Name(nr) => nr.as_name(),
|
ast::FieldKind::Name(nr) => nr.as_name(),
|
||||||
|
@ -3,7 +3,7 @@ use std::{ops::Index, sync::Arc};
|
|||||||
use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
|
use ra_arena::{impl_arena_id, map::ArenaMap, Arena, RawId};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast::{self, AttrsOwner, NameOwner},
|
ast::{self, AttrsOwner, NameOwner},
|
||||||
AstNode, AstPtr, SmolStr, SourceFile, TreeArc,
|
AstNode, AstPtr, SmolStr, SourceFile,
|
||||||
};
|
};
|
||||||
use test_utils::tested_by;
|
use test_utils::tested_by;
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ pub struct ImportSourceMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ImportSourcePtr = Either<AstPtr<ast::UseTree>, AstPtr<ast::ExternCrateItem>>;
|
type ImportSourcePtr = Either<AstPtr<ast::UseTree>, AstPtr<ast::ExternCrateItem>>;
|
||||||
type ImportSource = Either<TreeArc<ast::UseTree>, TreeArc<ast::ExternCrateItem>>;
|
type ImportSource = Either<ast::UseTree, ast::ExternCrateItem>;
|
||||||
|
|
||||||
impl ImportSourcePtr {
|
impl ImportSourcePtr {
|
||||||
fn to_node(self, file: &SourceFile) -> ImportSource {
|
fn to_node(self, file: &SourceFile) -> ImportSource {
|
||||||
@ -50,11 +50,11 @@ impl ImportSourceMap {
|
|||||||
|
|
||||||
pub(crate) fn get(&self, source: &ModuleSource, import: ImportId) -> ImportSource {
|
pub(crate) fn get(&self, source: &ModuleSource, import: ImportId) -> ImportSource {
|
||||||
let file = match source {
|
let file = match source {
|
||||||
ModuleSource::SourceFile(file) => &*file,
|
ModuleSource::SourceFile(file) => file.clone(),
|
||||||
ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
|
ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.map[import].to_node(file)
|
self.map[import].to_node(&file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,8 +76,8 @@ impl RawItems {
|
|||||||
source_map: ImportSourceMap::default(),
|
source_map: ImportSourceMap::default(),
|
||||||
};
|
};
|
||||||
if let Some(node) = db.parse_or_expand(file_id) {
|
if let Some(node) = db.parse_or_expand(file_id) {
|
||||||
if let Some(source_file) = ast::SourceFile::cast(&node) {
|
if let Some(source_file) = ast::SourceFile::cast(node) {
|
||||||
collector.process_module(None, &*source_file);
|
collector.process_module(None, source_file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Arc::new(collector.raw_items), Arc::new(collector.source_map))
|
(Arc::new(collector.raw_items), Arc::new(collector.source_map))
|
||||||
@ -188,7 +188,7 @@ struct RawItemsCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RawItemsCollector {
|
impl RawItemsCollector {
|
||||||
fn process_module(&mut self, current_module: Option<Module>, body: &impl ast::ModuleItemOwner) {
|
fn process_module(&mut self, current_module: Option<Module>, body: impl ast::ModuleItemOwner) {
|
||||||
for item_or_macro in body.items_with_macros() {
|
for item_or_macro in body.items_with_macros() {
|
||||||
match item_or_macro {
|
match item_or_macro {
|
||||||
ast::ItemOrMacro::Macro(m) => self.add_macro(current_module, m),
|
ast::ItemOrMacro::Macro(m) => self.add_macro(current_module, m),
|
||||||
@ -197,7 +197,7 @@ impl RawItemsCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_item(&mut self, current_module: Option<Module>, item: &ast::ModuleItem) {
|
fn add_item(&mut self, current_module: Option<Module>, item: ast::ModuleItem) {
|
||||||
let (kind, name) = match item.kind() {
|
let (kind, name) = match item.kind() {
|
||||||
ast::ModuleItemKind::Module(module) => {
|
ast::ModuleItemKind::Module(module) => {
|
||||||
self.add_module(current_module, module);
|
self.add_module(current_module, module);
|
||||||
@ -216,7 +216,7 @@ impl RawItemsCollector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ast::ModuleItemKind::StructDef(it) => {
|
ast::ModuleItemKind::StructDef(it) => {
|
||||||
let id = self.source_ast_id_map.ast_id(it);
|
let id = self.source_ast_id_map.ast_id(&it);
|
||||||
let name = it.name();
|
let name = it.name();
|
||||||
if it.is_union() {
|
if it.is_union() {
|
||||||
(DefKind::Union(id), name)
|
(DefKind::Union(id), name)
|
||||||
@ -225,22 +225,22 @@ impl RawItemsCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ModuleItemKind::EnumDef(it) => {
|
ast::ModuleItemKind::EnumDef(it) => {
|
||||||
(DefKind::Enum(self.source_ast_id_map.ast_id(it)), it.name())
|
(DefKind::Enum(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||||
}
|
}
|
||||||
ast::ModuleItemKind::FnDef(it) => {
|
ast::ModuleItemKind::FnDef(it) => {
|
||||||
(DefKind::Function(self.source_ast_id_map.ast_id(it)), it.name())
|
(DefKind::Function(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||||
}
|
}
|
||||||
ast::ModuleItemKind::TraitDef(it) => {
|
ast::ModuleItemKind::TraitDef(it) => {
|
||||||
(DefKind::Trait(self.source_ast_id_map.ast_id(it)), it.name())
|
(DefKind::Trait(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||||
}
|
}
|
||||||
ast::ModuleItemKind::TypeAliasDef(it) => {
|
ast::ModuleItemKind::TypeAliasDef(it) => {
|
||||||
(DefKind::TypeAlias(self.source_ast_id_map.ast_id(it)), it.name())
|
(DefKind::TypeAlias(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||||
}
|
}
|
||||||
ast::ModuleItemKind::ConstDef(it) => {
|
ast::ModuleItemKind::ConstDef(it) => {
|
||||||
(DefKind::Const(self.source_ast_id_map.ast_id(it)), it.name())
|
(DefKind::Const(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||||
}
|
}
|
||||||
ast::ModuleItemKind::StaticDef(it) => {
|
ast::ModuleItemKind::StaticDef(it) => {
|
||||||
(DefKind::Static(self.source_ast_id_map.ast_id(it)), it.name())
|
(DefKind::Static(self.source_ast_id_map.ast_id(&it)), it.name())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
@ -250,14 +250,14 @@ impl RawItemsCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_module(&mut self, current_module: Option<Module>, module: &ast::Module) {
|
fn add_module(&mut self, current_module: Option<Module>, module: ast::Module) {
|
||||||
let name = match module.name() {
|
let name = match module.name() {
|
||||||
Some(it) => it.as_name(),
|
Some(it) => it.as_name(),
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let attr_path = extract_mod_path_attribute(module);
|
let attr_path = extract_mod_path_attribute(&module);
|
||||||
let ast_id = self.source_ast_id_map.ast_id(module);
|
let ast_id = self.source_ast_id_map.ast_id(&module);
|
||||||
if module.has_semi() {
|
if module.has_semi() {
|
||||||
let item =
|
let item =
|
||||||
self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id, attr_path });
|
self.raw_items.modules.alloc(ModuleData::Declaration { name, ast_id, attr_path });
|
||||||
@ -278,10 +278,10 @@ impl RawItemsCollector {
|
|||||||
tested_by!(name_res_works_for_broken_modules);
|
tested_by!(name_res_works_for_broken_modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_use_item(&mut self, current_module: Option<Module>, use_item: &ast::UseItem) {
|
fn add_use_item(&mut self, current_module: Option<Module>, use_item: ast::UseItem) {
|
||||||
let is_prelude = use_item.has_atom_attr("prelude_import");
|
let is_prelude = use_item.has_atom_attr("prelude_import");
|
||||||
|
|
||||||
Path::expand_use_item(use_item, |path, use_tree, is_glob, alias| {
|
Path::expand_use_item(&use_item, |path, use_tree, is_glob, alias| {
|
||||||
let import_data =
|
let import_data =
|
||||||
ImportData { path, alias, is_glob, is_prelude, is_extern_crate: false };
|
ImportData { path, alias, is_glob, is_prelude, is_extern_crate: false };
|
||||||
self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree)));
|
self.push_import(current_module, import_data, Either::A(AstPtr::new(use_tree)));
|
||||||
@ -291,11 +291,11 @@ impl RawItemsCollector {
|
|||||||
fn add_extern_crate_item(
|
fn add_extern_crate_item(
|
||||||
&mut self,
|
&mut self,
|
||||||
current_module: Option<Module>,
|
current_module: Option<Module>,
|
||||||
extern_crate: &ast::ExternCrateItem,
|
extern_crate: ast::ExternCrateItem,
|
||||||
) {
|
) {
|
||||||
if let Some(name_ref) = extern_crate.name_ref() {
|
if let Some(name_ref) = extern_crate.name_ref() {
|
||||||
let path = Path::from_name_ref(name_ref);
|
let path = Path::from_name_ref(&name_ref);
|
||||||
let alias = extern_crate.alias().and_then(|a| a.name()).map(AsName::as_name);
|
let alias = extern_crate.alias().and_then(|a| a.name()).map(|it| it.as_name());
|
||||||
let import_data = ImportData {
|
let import_data = ImportData {
|
||||||
path,
|
path,
|
||||||
alias,
|
alias,
|
||||||
@ -303,18 +303,18 @@ impl RawItemsCollector {
|
|||||||
is_prelude: false,
|
is_prelude: false,
|
||||||
is_extern_crate: true,
|
is_extern_crate: true,
|
||||||
};
|
};
|
||||||
self.push_import(current_module, import_data, Either::B(AstPtr::new(extern_crate)));
|
self.push_import(current_module, import_data, Either::B(AstPtr::new(&extern_crate)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_macro(&mut self, current_module: Option<Module>, m: &ast::MacroCall) {
|
fn add_macro(&mut self, current_module: Option<Module>, m: ast::MacroCall) {
|
||||||
let path = match m.path().and_then(Path::from_ast) {
|
let path = match m.path().and_then(Path::from_ast) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = m.name().map(|it| it.as_name());
|
let name = m.name().map(|it| it.as_name());
|
||||||
let ast_id = self.source_ast_id_map.ast_id(m);
|
let ast_id = self.source_ast_id_map.ast_id(&m);
|
||||||
let export = m.has_atom_attr("macro_export");
|
let export = m.has_atom_attr("macro_export");
|
||||||
let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export });
|
let m = self.raw_items.macros.alloc(MacroData { ast_id, path, name, export });
|
||||||
self.push_item(current_module, RawItem::Macro(m));
|
self.push_item(current_module, RawItem::Macro(m));
|
||||||
|
@ -47,9 +47,9 @@ pub enum PathKind {
|
|||||||
|
|
||||||
impl Path {
|
impl Path {
|
||||||
/// Calls `cb` with all paths, represented by this use item.
|
/// Calls `cb` with all paths, represented by this use item.
|
||||||
pub fn expand_use_item<'a>(
|
pub fn expand_use_item(
|
||||||
item: &'a ast::UseItem,
|
item: &ast::UseItem,
|
||||||
mut cb: impl FnMut(Path, &'a ast::UseTree, bool, Option<Name>),
|
mut cb: impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
||||||
) {
|
) {
|
||||||
if let Some(tree) = item.use_tree() {
|
if let Some(tree) = item.use_tree() {
|
||||||
expand_use_tree(None, tree, &mut cb);
|
expand_use_tree(None, tree, &mut cb);
|
||||||
@ -57,7 +57,7 @@ impl Path {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
/// Converts an `ast::Path` to `Path`. Works with use trees.
|
||||||
pub fn from_ast(mut path: &ast::Path) -> Option<Path> {
|
pub fn from_ast(mut path: ast::Path) -> Option<Path> {
|
||||||
let mut kind = PathKind::Plain;
|
let mut kind = PathKind::Plain;
|
||||||
let mut segments = Vec::new();
|
let mut segments = Vec::new();
|
||||||
loop {
|
loop {
|
||||||
@ -87,7 +87,7 @@ impl Path {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
path = match qualifier(path) {
|
path = match qualifier(&path) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => break,
|
None => break,
|
||||||
};
|
};
|
||||||
@ -95,7 +95,7 @@ impl Path {
|
|||||||
segments.reverse();
|
segments.reverse();
|
||||||
return Some(Path { kind, segments });
|
return Some(Path { kind, segments });
|
||||||
|
|
||||||
fn qualifier(path: &ast::Path) -> Option<&ast::Path> {
|
fn qualifier(path: &ast::Path) -> Option<ast::Path> {
|
||||||
if let Some(q) = path.qualifier() {
|
if let Some(q) = path.qualifier() {
|
||||||
return Some(q);
|
return Some(q);
|
||||||
}
|
}
|
||||||
@ -136,7 +136,7 @@ impl Path {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GenericArgs {
|
impl GenericArgs {
|
||||||
pub(crate) fn from_ast(node: &ast::TypeArgList) -> Option<GenericArgs> {
|
pub(crate) fn from_ast(node: ast::TypeArgList) -> Option<GenericArgs> {
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
for type_arg in node.type_args() {
|
for type_arg in node.type_args() {
|
||||||
let type_ref = TypeRef::from_ast_opt(type_arg.type_ref());
|
let type_ref = TypeRef::from_ast_opt(type_arg.type_ref());
|
||||||
@ -160,10 +160,10 @@ impl From<Name> for Path {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expand_use_tree<'a>(
|
fn expand_use_tree(
|
||||||
prefix: Option<Path>,
|
prefix: Option<Path>,
|
||||||
tree: &'a ast::UseTree,
|
tree: ast::UseTree,
|
||||||
cb: &mut impl FnMut(Path, &'a ast::UseTree, bool, Option<Name>),
|
cb: &mut impl FnMut(Path, &ast::UseTree, bool, Option<Name>),
|
||||||
) {
|
) {
|
||||||
if let Some(use_tree_list) = tree.use_tree_list() {
|
if let Some(use_tree_list) = tree.use_tree_list() {
|
||||||
let prefix = match tree.path() {
|
let prefix = match tree.path() {
|
||||||
@ -188,7 +188,7 @@ fn expand_use_tree<'a>(
|
|||||||
if let Some(segment) = ast_path.segment() {
|
if let Some(segment) = ast_path.segment() {
|
||||||
if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
|
if segment.kind() == Some(ast::PathSegmentKind::SelfKw) {
|
||||||
if let Some(prefix) = prefix {
|
if let Some(prefix) = prefix {
|
||||||
cb(prefix, tree, false, alias);
|
cb(prefix, &tree, false, alias);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -196,7 +196,7 @@ fn expand_use_tree<'a>(
|
|||||||
}
|
}
|
||||||
if let Some(path) = convert_path(prefix, ast_path) {
|
if let Some(path) = convert_path(prefix, ast_path) {
|
||||||
let is_glob = tree.has_star();
|
let is_glob = tree.has_star();
|
||||||
cb(path, tree, is_glob, alias)
|
cb(path, &tree, is_glob, alias)
|
||||||
}
|
}
|
||||||
// FIXME: report errors somewhere
|
// FIXME: report errors somewhere
|
||||||
// We get here if we do
|
// We get here if we do
|
||||||
@ -204,7 +204,7 @@ fn expand_use_tree<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_path(prefix: Option<Path>, path: &ast::Path) -> Option<Path> {
|
fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
|
||||||
let prefix =
|
let prefix =
|
||||||
if let Some(qual) = path.qualifier() { Some(convert_path(prefix, qual)?) } else { prefix };
|
if let Some(qual) = path.qualifier() { Some(convert_path(prefix, qual)?) } else { prefix };
|
||||||
let segment = path.segment()?;
|
let segment = path.segment()?;
|
||||||
|
@ -37,7 +37,7 @@ pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Option<Mod
|
|||||||
pub fn module_from_declaration(
|
pub fn module_from_declaration(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
decl: &ast::Module,
|
decl: ast::Module,
|
||||||
) -> Option<Module> {
|
) -> Option<Module> {
|
||||||
let parent_module = module_from_file_id(db, file_id);
|
let parent_module = module_from_file_id(db, file_id);
|
||||||
let child_name = decl.name();
|
let child_name = decl.name();
|
||||||
@ -50,8 +50,8 @@ pub fn module_from_declaration(
|
|||||||
/// Locates the module by position in the source code.
|
/// Locates the module by position in the source code.
|
||||||
pub fn module_from_position(db: &impl HirDatabase, position: FilePosition) -> Option<Module> {
|
pub fn module_from_position(db: &impl HirDatabase, position: FilePosition) -> Option<Module> {
|
||||||
let parse = db.parse(position.file_id);
|
let parse = db.parse(position.file_id);
|
||||||
match find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset) {
|
match &find_node_at_offset::<ast::Module>(parse.tree().syntax(), position.offset) {
|
||||||
Some(m) if !m.has_semi() => module_from_inline(db, position.file_id, m),
|
Some(m) if !m.has_semi() => module_from_inline(db, position.file_id, m.clone()),
|
||||||
_ => module_from_file_id(db, position.file_id),
|
_ => module_from_file_id(db, position.file_id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,12 +59,12 @@ pub fn module_from_position(db: &impl HirDatabase, position: FilePosition) -> Op
|
|||||||
fn module_from_inline(
|
fn module_from_inline(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
module: &ast::Module,
|
module: ast::Module,
|
||||||
) -> Option<Module> {
|
) -> Option<Module> {
|
||||||
assert!(!module.has_semi());
|
assert!(!module.has_semi());
|
||||||
let file_id = file_id.into();
|
let file_id = file_id.into();
|
||||||
let ast_id_map = db.ast_id_map(file_id);
|
let ast_id_map = db.ast_id_map(file_id);
|
||||||
let item_id = ast_id_map.ast_id(module).with_file_id(file_id);
|
let item_id = ast_id_map.ast_id(&module).with_file_id(file_id);
|
||||||
module_from_source(db, file_id, Some(item_id))
|
module_from_source(db, file_id, Some(item_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,16 +127,16 @@ fn try_get_resolver_for_node(
|
|||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
node: &SyntaxNode,
|
node: &SyntaxNode,
|
||||||
) -> Option<Resolver> {
|
) -> Option<Resolver> {
|
||||||
if let Some(module) = ast::Module::cast(node) {
|
if let Some(module) = ast::Module::cast(node.clone()) {
|
||||||
Some(module_from_declaration(db, file_id, module)?.resolver(db))
|
Some(module_from_declaration(db, file_id, module)?.resolver(db))
|
||||||
} else if let Some(_) = ast::SourceFile::cast(node) {
|
} else if let Some(_) = ast::SourceFile::cast(node.clone()) {
|
||||||
Some(module_from_source(db, file_id.into(), None)?.resolver(db))
|
Some(module_from_source(db, file_id.into(), None)?.resolver(db))
|
||||||
} else if let Some(s) = ast::StructDef::cast(node) {
|
} else if let Some(s) = ast::StructDef::cast(node.clone()) {
|
||||||
let module = module_from_child_node(db, file_id, s.syntax())?;
|
let module = module_from_child_node(db, file_id, s.syntax())?;
|
||||||
Some(struct_from_module(db, module, s).resolver(db))
|
Some(struct_from_module(db, module, &s).resolver(db))
|
||||||
} else if let Some(e) = ast::EnumDef::cast(node) {
|
} else if let Some(e) = ast::EnumDef::cast(node.clone()) {
|
||||||
let module = module_from_child_node(db, file_id, e.syntax())?;
|
let module = module_from_child_node(db, file_id, e.syntax())?;
|
||||||
Some(enum_from_module(db, module, e).resolver(db))
|
Some(enum_from_module(db, module, &e).resolver(db))
|
||||||
} else if node.kind() == FN_DEF || node.kind() == CONST_DEF || node.kind() == STATIC_DEF {
|
} else if node.kind() == FN_DEF || node.kind() == CONST_DEF || node.kind() == STATIC_DEF {
|
||||||
Some(def_with_body_from_child_node(db, file_id, node)?.resolver(db))
|
Some(def_with_body_from_child_node(db, file_id, node)?.resolver(db))
|
||||||
} else {
|
} else {
|
||||||
@ -153,14 +153,14 @@ fn def_with_body_from_child_node(
|
|||||||
let module = module_from_child_node(db, file_id, node)?;
|
let module = module_from_child_node(db, file_id, node)?;
|
||||||
let ctx = LocationCtx::new(db, module, file_id.into());
|
let ctx = LocationCtx::new(db, module, file_id.into());
|
||||||
node.ancestors().find_map(|node| {
|
node.ancestors().find_map(|node| {
|
||||||
if let Some(def) = ast::FnDef::cast(node) {
|
if let Some(def) = ast::FnDef::cast(node.clone()) {
|
||||||
return Some(Function { id: ctx.to_def(def) }.into());
|
return Some(Function { id: ctx.to_def(&def) }.into());
|
||||||
}
|
}
|
||||||
if let Some(def) = ast::ConstDef::cast(node) {
|
if let Some(def) = ast::ConstDef::cast(node.clone()) {
|
||||||
return Some(Const { id: ctx.to_def(def) }.into());
|
return Some(Const { id: ctx.to_def(&def) }.into());
|
||||||
}
|
}
|
||||||
if let Some(def) = ast::StaticDef::cast(node) {
|
if let Some(def) = ast::StaticDef::cast(node.clone()) {
|
||||||
return Some(Static { id: ctx.to_def(def) }.into());
|
return Some(Static { id: ctx.to_def(&def) }.into());
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
})
|
})
|
||||||
@ -237,7 +237,7 @@ impl SourceAnalyzer {
|
|||||||
SourceAnalyzer {
|
SourceAnalyzer {
|
||||||
resolver: node
|
resolver: node
|
||||||
.ancestors()
|
.ancestors()
|
||||||
.find_map(|node| try_get_resolver_for_node(db, file_id, node))
|
.find_map(|node| try_get_resolver_for_node(db, file_id, &node))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
body_source_map: None,
|
body_source_map: None,
|
||||||
infer: None,
|
infer: None,
|
||||||
@ -257,17 +257,17 @@ impl SourceAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
|
pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
|
||||||
let expr_id = self.body_source_map.as_ref()?.node_expr(call.into())?;
|
let expr_id = self.body_source_map.as_ref()?.node_expr(&call.clone().into())?;
|
||||||
self.infer.as_ref()?.method_resolution(expr_id)
|
self.infer.as_ref()?.method_resolution(expr_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> {
|
pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> {
|
||||||
let expr_id = self.body_source_map.as_ref()?.node_expr(field.into())?;
|
let expr_id = self.body_source_map.as_ref()?.node_expr(&field.clone().into())?;
|
||||||
self.infer.as_ref()?.field_resolution(expr_id)
|
self.infer.as_ref()?.field_resolution(expr_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_variant(&self, struct_lit: &ast::StructLit) -> Option<crate::VariantDef> {
|
pub fn resolve_variant(&self, struct_lit: &ast::StructLit) -> Option<crate::VariantDef> {
|
||||||
let expr_id = self.body_source_map.as_ref()?.node_expr(struct_lit.into())?;
|
let expr_id = self.body_source_map.as_ref()?.node_expr(&struct_lit.clone().into())?;
|
||||||
self.infer.as_ref()?.variant_resolution(expr_id)
|
self.infer.as_ref()?.variant_resolution(expr_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,18 +290,18 @@ impl SourceAnalyzer {
|
|||||||
|
|
||||||
pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> {
|
pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> {
|
||||||
if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) {
|
if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) {
|
||||||
let expr_id = self.body_source_map.as_ref()?.node_expr(path_expr.into())?;
|
let expr_id = self.body_source_map.as_ref()?.node_expr(&path_expr.into())?;
|
||||||
if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) {
|
if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) {
|
||||||
return Some(PathResolution::AssocItem(assoc));
|
return Some(PathResolution::AssocItem(assoc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) {
|
if let Some(path_pat) = path.syntax().parent().and_then(ast::PathPat::cast) {
|
||||||
let pat_id = self.body_source_map.as_ref()?.node_pat(path_pat.into())?;
|
let pat_id = self.body_source_map.as_ref()?.node_pat(&path_pat.into())?;
|
||||||
if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) {
|
if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_pat(pat_id) {
|
||||||
return Some(PathResolution::AssocItem(assoc));
|
return Some(PathResolution::AssocItem(assoc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let hir_path = crate::Path::from_ast(path)?;
|
let hir_path = crate::Path::from_ast(path.clone())?;
|
||||||
let res = self.resolver.resolve_path_without_assoc_items(db, &hir_path);
|
let res = self.resolver.resolve_path_without_assoc_items(db, &hir_path);
|
||||||
let res = res.clone().take_types().or_else(|| res.take_values())?;
|
let res = res.clone().take_types().or_else(|| res.take_values())?;
|
||||||
let res = match res {
|
let res = match res {
|
||||||
@ -343,12 +343,12 @@ impl SourceAnalyzer {
|
|||||||
// FIXME: at least, this should work with any DefWithBody, but ideally
|
// FIXME: at least, this should work with any DefWithBody, but ideally
|
||||||
// this should be hir-based altogether
|
// this should be hir-based altogether
|
||||||
let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
|
let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
|
||||||
let ptr = Either::A(AstPtr::new(pat.into()));
|
let ptr = Either::A(AstPtr::new(&ast::Pat::from(pat.clone())));
|
||||||
fn_def
|
fn_def
|
||||||
.syntax()
|
.syntax()
|
||||||
.descendants()
|
.descendants()
|
||||||
.filter_map(ast::NameRef::cast)
|
.filter_map(ast::NameRef::cast)
|
||||||
.filter(|name_ref| match self.resolve_local_name(*name_ref) {
|
.filter(|name_ref| match self.resolve_local_name(&name_ref) {
|
||||||
None => false,
|
None => false,
|
||||||
Some(entry) => entry.ptr() == ptr,
|
Some(entry) => entry.ptr() == ptr,
|
||||||
})
|
})
|
||||||
@ -411,7 +411,7 @@ fn scope_for(
|
|||||||
node: &SyntaxNode,
|
node: &SyntaxNode,
|
||||||
) -> Option<ScopeId> {
|
) -> Option<ScopeId> {
|
||||||
node.ancestors()
|
node.ancestors()
|
||||||
.map(SyntaxNodePtr::new)
|
.map(|it| SyntaxNodePtr::new(&it))
|
||||||
.filter_map(|ptr| source_map.syntax_expr(ptr))
|
.filter_map(|ptr| source_map.syntax_expr(ptr))
|
||||||
.find_map(|it| scopes.scope_for(it))
|
.find_map(|it| scopes.scope_for(it))
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use ra_arena::{impl_arena_id, Arena, RawId};
|
use ra_arena::{impl_arena_id, Arena, RawId};
|
||||||
use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxNodePtr, TreeArc};
|
use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxNodePtr};
|
||||||
|
|
||||||
use crate::{AstDatabase, HirFileId};
|
use crate::{AstDatabase, HirFileId};
|
||||||
|
|
||||||
@ -42,9 +42,9 @@ impl<N: AstNode> AstId<N> {
|
|||||||
self.file_id
|
self.file_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn to_node(&self, db: &impl AstDatabase) -> TreeArc<N> {
|
pub(crate) fn to_node(&self, db: &impl AstDatabase) -> N {
|
||||||
let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.raw);
|
let syntax_node = db.ast_id_to_node(self.file_id, self.file_ast_id.raw);
|
||||||
N::cast(&syntax_node).unwrap().to_owned()
|
N::cast(syntax_node).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ pub struct AstIdMap {
|
|||||||
impl AstIdMap {
|
impl AstIdMap {
|
||||||
pub(crate) fn ast_id_map_query(db: &impl AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
|
pub(crate) fn ast_id_map_query(db: &impl AstDatabase, file_id: HirFileId) -> Arc<AstIdMap> {
|
||||||
let map = if let Some(node) = db.parse_or_expand(file_id) {
|
let map = if let Some(node) = db.parse_or_expand(file_id) {
|
||||||
AstIdMap::from_source(&*node)
|
AstIdMap::from_source(&node)
|
||||||
} else {
|
} else {
|
||||||
AstIdMap::default()
|
AstIdMap::default()
|
||||||
};
|
};
|
||||||
@ -104,9 +104,9 @@ impl AstIdMap {
|
|||||||
db: &impl AstDatabase,
|
db: &impl AstDatabase,
|
||||||
file_id: HirFileId,
|
file_id: HirFileId,
|
||||||
ast_id: ErasedFileAstId,
|
ast_id: ErasedFileAstId,
|
||||||
) -> TreeArc<SyntaxNode> {
|
) -> SyntaxNode {
|
||||||
let node = db.parse_or_expand(file_id).unwrap();
|
let node = db.parse_or_expand(file_id).unwrap();
|
||||||
db.ast_id_map(file_id).arena[ast_id].to_node(&*node).to_owned()
|
db.ast_id_map(file_id).arena[ast_id].to_node(&node)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> {
|
pub(crate) fn ast_id<N: AstNode>(&self, item: &N) -> FileAstId<N> {
|
||||||
@ -131,7 +131,7 @@ impl AstIdMap {
|
|||||||
// change parent's id. This means that, say, adding a new function to a
|
// change parent's id. This means that, say, adding a new function to a
|
||||||
// trait does not change ids of top-level items, which helps caching.
|
// trait does not change ids of top-level items, which helps caching.
|
||||||
bfs(node, |it| {
|
bfs(node, |it| {
|
||||||
if let Some(module_item) = ast::ModuleItem::cast(it) {
|
if let Some(module_item) = ast::ModuleItem::cast(it.clone()) {
|
||||||
res.alloc(module_item.syntax());
|
res.alloc(module_item.syntax());
|
||||||
} else if let Some(macro_call) = ast::MacroCall::cast(it) {
|
} else if let Some(macro_call) = ast::MacroCall::cast(it) {
|
||||||
res.alloc(macro_call.syntax());
|
res.alloc(macro_call.syntax());
|
||||||
@ -146,8 +146,8 @@ impl AstIdMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Walks the subtree in bfs order, calling `f` for each node.
|
/// Walks the subtree in bfs order, calling `f` for each node.
|
||||||
fn bfs(node: &SyntaxNode, mut f: impl FnMut(&SyntaxNode)) {
|
fn bfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode)) {
|
||||||
let mut curr_layer = vec![node];
|
let mut curr_layer = vec![node.clone()];
|
||||||
let mut next_layer = vec![];
|
let mut next_layer = vec![];
|
||||||
while !curr_layer.is_empty() {
|
while !curr_layer.is_empty() {
|
||||||
curr_layer.drain(..).for_each(|node| {
|
curr_layer.drain(..).for_each(|node| {
|
||||||
|
@ -31,9 +31,9 @@ impl TraitData {
|
|||||||
item_list
|
item_list
|
||||||
.impl_items()
|
.impl_items()
|
||||||
.map(|item_node| match item_node.kind() {
|
.map(|item_node| match item_node.kind() {
|
||||||
ast::ImplItemKind::FnDef(it) => Function { id: ctx.to_def(it) }.into(),
|
ast::ImplItemKind::FnDef(it) => Function { id: ctx.to_def(&it) }.into(),
|
||||||
ast::ImplItemKind::ConstDef(it) => Const { id: ctx.to_def(it) }.into(),
|
ast::ImplItemKind::ConstDef(it) => Const { id: ctx.to_def(&it) }.into(),
|
||||||
ast::ImplItemKind::TypeAliasDef(it) => TypeAlias { id: ctx.to_def(it) }.into(),
|
ast::ImplItemKind::TypeAliasDef(it) => TypeAlias { id: ctx.to_def(&it) }.into(),
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
|
@ -3086,7 +3086,7 @@ fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
|
|||||||
let file = db.parse(pos.file_id).ok().unwrap();
|
let file = db.parse(pos.file_id).ok().unwrap();
|
||||||
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
|
let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap();
|
||||||
let analyzer = SourceAnalyzer::new(db, pos.file_id, expr.syntax(), Some(pos.offset));
|
let analyzer = SourceAnalyzer::new(db, pos.file_id, expr.syntax(), Some(pos.offset));
|
||||||
let ty = analyzer.type_of(db, expr).unwrap();
|
let ty = analyzer.type_of(db, &expr).unwrap();
|
||||||
ty.display(db).to_string()
|
ty.display(db).to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3126,7 +3126,7 @@ fn infer(content: &str) -> String {
|
|||||||
types.sort_by_key(|(ptr, _)| (ptr.range().start(), ptr.range().end()));
|
types.sort_by_key(|(ptr, _)| (ptr.range().start(), ptr.range().end()));
|
||||||
for (syntax_ptr, ty) in &types {
|
for (syntax_ptr, ty) in &types {
|
||||||
let node = syntax_ptr.to_node(source_file.syntax());
|
let node = syntax_ptr.to_node(source_file.syntax());
|
||||||
let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node) {
|
let (range, text) = if let Some(self_param) = ast::SelfParam::cast(node.clone()) {
|
||||||
(self_param.self_kw_token().range(), "self".to_string())
|
(self_param.self_kw_token().range(), "self".to_string())
|
||||||
} else {
|
} else {
|
||||||
(syntax_ptr.range(), node.text().to_string().replace("\n", " "))
|
(syntax_ptr.range(), node.text().to_string().replace("\n", " "))
|
||||||
@ -3137,7 +3137,7 @@ fn infer(content: &str) -> String {
|
|||||||
|
|
||||||
for node in source_file.syntax().descendants() {
|
for node in source_file.syntax().descendants() {
|
||||||
if node.kind() == FN_DEF || node.kind() == CONST_DEF || node.kind() == STATIC_DEF {
|
if node.kind() == FN_DEF || node.kind() == CONST_DEF || node.kind() == STATIC_DEF {
|
||||||
let analyzer = SourceAnalyzer::new(&db, file_id, node, None);
|
let analyzer = SourceAnalyzer::new(&db, file_id, &node, None);
|
||||||
infer_def(analyzer.inference_result(), analyzer.body_source_map());
|
infer_def(analyzer.inference_result(), analyzer.body_source_map());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3179,7 +3179,7 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
|
|||||||
let node =
|
let node =
|
||||||
algo::find_token_at_offset(file.syntax(), pos.offset).right_biased().unwrap().parent();
|
algo::find_token_at_offset(file.syntax(), pos.offset).right_biased().unwrap().parent();
|
||||||
let events = db.log_executed(|| {
|
let events = db.log_executed(|| {
|
||||||
SourceAnalyzer::new(&db, pos.file_id, node, None);
|
SourceAnalyzer::new(&db, pos.file_id, &node, None);
|
||||||
});
|
});
|
||||||
assert!(format!("{:?}", events).contains("infer"))
|
assert!(format!("{:?}", events).contains("infer"))
|
||||||
}
|
}
|
||||||
@ -3200,7 +3200,7 @@ fn typing_whitespace_inside_a_function_should_not_invalidate_types() {
|
|||||||
let node =
|
let node =
|
||||||
algo::find_token_at_offset(file.syntax(), pos.offset).right_biased().unwrap().parent();
|
algo::find_token_at_offset(file.syntax(), pos.offset).right_biased().unwrap().parent();
|
||||||
let events = db.log_executed(|| {
|
let events = db.log_executed(|| {
|
||||||
SourceAnalyzer::new(&db, pos.file_id, node, None);
|
SourceAnalyzer::new(&db, pos.file_id, &node, None);
|
||||||
});
|
});
|
||||||
assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events)
|
assert!(!format!("{:?}", events).contains("infer"), "{:#?}", events)
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ pub enum TypeRef {
|
|||||||
|
|
||||||
impl TypeRef {
|
impl TypeRef {
|
||||||
/// Converts an `ast::TypeRef` to a `hir::TypeRef`.
|
/// Converts an `ast::TypeRef` to a `hir::TypeRef`.
|
||||||
pub(crate) fn from_ast(node: &ast::TypeRef) -> Self {
|
pub(crate) fn from_ast(node: ast::TypeRef) -> Self {
|
||||||
use ra_syntax::ast::TypeRefKind::*;
|
use ra_syntax::ast::TypeRefKind::*;
|
||||||
match node.kind() {
|
match node.kind() {
|
||||||
ParenType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
|
ParenType(inner) => TypeRef::from_ast_opt(inner.type_ref()),
|
||||||
@ -95,7 +95,7 @@ impl TypeRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_ast_opt(node: Option<&ast::TypeRef>) -> Self {
|
pub(crate) fn from_ast_opt(node: Option<ast::TypeRef>) -> Self {
|
||||||
if let Some(node) = node {
|
if let Some(node) = node {
|
||||||
TypeRef::from_ast(node)
|
TypeRef::from_ast(node)
|
||||||
} else {
|
} else {
|
||||||
|
@ -11,24 +11,24 @@ use crate::{db::RootDatabase, CallInfo, FilePosition, FunctionSignature};
|
|||||||
/// Computes parameter information for the given call expression.
|
/// Computes parameter information for the given call expression.
|
||||||
pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
|
pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> {
|
||||||
let parse = db.parse(position.file_id);
|
let parse = db.parse(position.file_id);
|
||||||
let syntax = parse.tree().syntax();
|
let syntax = parse.tree().syntax().clone();
|
||||||
|
|
||||||
// Find the calling expression and it's NameRef
|
// Find the calling expression and it's NameRef
|
||||||
let calling_node = FnCallNode::with_node(syntax, position.offset)?;
|
let calling_node = FnCallNode::with_node(&syntax, position.offset)?;
|
||||||
let name_ref = calling_node.name_ref()?;
|
let name_ref = calling_node.name_ref()?;
|
||||||
|
|
||||||
let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
|
let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
|
||||||
let function = match calling_node {
|
let function = match &calling_node {
|
||||||
FnCallNode::CallExpr(expr) => {
|
FnCallNode::CallExpr(expr) => {
|
||||||
//FIXME: apply subst
|
//FIXME: apply subst
|
||||||
let (callable_def, _subst) = analyzer.type_of(db, expr.expr()?)?.as_callable()?;
|
let (callable_def, _subst) = analyzer.type_of(db, &expr.expr()?)?.as_callable()?;
|
||||||
match callable_def {
|
match callable_def {
|
||||||
hir::CallableDef::Function(it) => it,
|
hir::CallableDef::Function(it) => it,
|
||||||
//FIXME: handle other callables
|
//FIXME: handle other callables
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FnCallNode::MethodCallExpr(expr) => analyzer.resolve_method_call(expr)?,
|
FnCallNode::MethodCallExpr(expr) => analyzer.resolve_method_call(&expr)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut call_info = CallInfo::new(db, function);
|
let mut call_info = CallInfo::new(db, function);
|
||||||
@ -73,13 +73,13 @@ pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option<Cal
|
|||||||
Some(call_info)
|
Some(call_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum FnCallNode<'a> {
|
enum FnCallNode {
|
||||||
CallExpr(&'a ast::CallExpr),
|
CallExpr(ast::CallExpr),
|
||||||
MethodCallExpr(&'a ast::MethodCallExpr),
|
MethodCallExpr(ast::MethodCallExpr),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FnCallNode<'a> {
|
impl FnCallNode {
|
||||||
fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> {
|
fn with_node(syntax: &SyntaxNode, offset: TextUnit) -> Option<FnCallNode> {
|
||||||
if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) {
|
if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) {
|
||||||
return Some(FnCallNode::CallExpr(expr));
|
return Some(FnCallNode::CallExpr(expr));
|
||||||
}
|
}
|
||||||
@ -89,8 +89,8 @@ impl<'a> FnCallNode<'a> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name_ref(&self) -> Option<&'a ast::NameRef> {
|
fn name_ref(&self) -> Option<ast::NameRef> {
|
||||||
match *self {
|
match self {
|
||||||
FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() {
|
FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() {
|
||||||
ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
|
ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
@ -102,8 +102,8 @@ impl<'a> FnCallNode<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arg_list(&self) -> Option<&'a ast::ArgList> {
|
fn arg_list(&self) -> Option<ast::ArgList> {
|
||||||
match *self {
|
match self {
|
||||||
FnCallNode::CallExpr(expr) => expr.arg_list(),
|
FnCallNode::CallExpr(expr) => expr.arg_list(),
|
||||||
FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
|
FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,11 @@ use rustc_hash::FxHashSet;
|
|||||||
|
|
||||||
/// Complete dot accesses, i.e. fields or methods (currently only fields).
|
/// Complete dot accesses, i.e. fields or methods (currently only fields).
|
||||||
pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let receiver_ty = match ctx.dot_receiver.and_then(|it| ctx.analyzer.type_of(ctx.db, it)) {
|
let receiver_ty =
|
||||||
Some(it) => it,
|
match ctx.dot_receiver.as_ref().and_then(|it| ctx.analyzer.type_of(ctx.db, it)) {
|
||||||
None => return,
|
Some(it) => it,
|
||||||
};
|
None => return,
|
||||||
|
};
|
||||||
if !ctx.is_call {
|
if !ctx.is_call {
|
||||||
complete_fields(acc, ctx, receiver_ty.clone());
|
complete_fields(acc, ctx, receiver_ty.clone());
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
|
|||||||
let _ = visitor_ctx(&mut params)
|
let _ = visitor_ctx(&mut params)
|
||||||
.visit::<ast::SourceFile, _>(process)
|
.visit::<ast::SourceFile, _>(process)
|
||||||
.visit::<ast::ItemList, _>(process)
|
.visit::<ast::ItemList, _>(process)
|
||||||
.accept(node);
|
.accept(&node);
|
||||||
}
|
}
|
||||||
params
|
params
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -38,10 +38,7 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
|
|||||||
.add_to(acc)
|
.add_to(acc)
|
||||||
});
|
});
|
||||||
|
|
||||||
fn process<'a, N: ast::FnDefOwner>(
|
fn process<N: ast::FnDefOwner>(node: N, params: &mut FxHashMap<String, (u32, ast::Param)>) {
|
||||||
node: &'a N,
|
|
||||||
params: &mut FxHashMap<String, (u32, &'a ast::Param)>,
|
|
||||||
) {
|
|
||||||
node.functions().filter_map(|it| it.param_list()).flat_map(|it| it.params()).for_each(
|
node.functions().filter_map(|it| it.param_list()).flat_map(|it| it.params()).for_each(
|
||||||
|param| {
|
|param| {
|
||||||
let text = param.syntax().text().to_string();
|
let text = param.syntax().text().to_string();
|
||||||
|
@ -52,7 +52,7 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_def = match ctx.function_syntax {
|
let fn_def = match &ctx.function_syntax {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
@ -65,7 +65,7 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
|
|||||||
acc.add(keyword(ctx, "else", "else {$0}"));
|
acc.add(keyword(ctx, "else", "else {$0}"));
|
||||||
acc.add(keyword(ctx, "else if", "else if $0 {}"));
|
acc.add(keyword(ctx, "else if", "else if $0 {}"));
|
||||||
}
|
}
|
||||||
if is_in_loop_body(ctx.token) {
|
if is_in_loop_body(&ctx.token) {
|
||||||
if ctx.can_be_stmt {
|
if ctx.can_be_stmt {
|
||||||
acc.add(keyword(ctx, "continue", "continue;"));
|
acc.add(keyword(ctx, "continue", "continue;"));
|
||||||
acc.add(keyword(ctx, "break", "break;"));
|
acc.add(keyword(ctx, "break", "break;"));
|
||||||
@ -74,19 +74,19 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
|
|||||||
acc.add(keyword(ctx, "break", "break"));
|
acc.add(keyword(ctx, "break", "break"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
acc.add_all(complete_return(ctx, fn_def, ctx.can_be_stmt));
|
acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_in_loop_body(leaf: SyntaxToken) -> bool {
|
fn is_in_loop_body(leaf: &SyntaxToken) -> bool {
|
||||||
for node in leaf.parent().ancestors() {
|
for node in leaf.parent().ancestors() {
|
||||||
if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
|
if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let loop_body = visitor()
|
let loop_body = visitor()
|
||||||
.visit::<ast::ForExpr, _>(LoopBodyOwner::loop_body)
|
.visit::<ast::ForExpr, _>(|it| it.loop_body())
|
||||||
.visit::<ast::WhileExpr, _>(LoopBodyOwner::loop_body)
|
.visit::<ast::WhileExpr, _>(|it| it.loop_body())
|
||||||
.visit::<ast::LoopExpr, _>(LoopBodyOwner::loop_body)
|
.visit::<ast::LoopExpr, _>(|it| it.loop_body())
|
||||||
.accept(node);
|
.accept(&node);
|
||||||
if let Some(Some(body)) = loop_body {
|
if let Some(Some(body)) = loop_body {
|
||||||
if leaf.range().is_subrange(&body.syntax().range()) {
|
if leaf.range().is_subrange(&body.syntax().range()) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -11,7 +11,8 @@ use ra_text_edit::TextEditBuilder;
|
|||||||
|
|
||||||
fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
|
fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder {
|
||||||
let edit = {
|
let edit = {
|
||||||
let receiver_range = ctx.dot_receiver.expect("no receiver available").syntax().range();
|
let receiver_range =
|
||||||
|
ctx.dot_receiver.as_ref().expect("no receiver available").syntax().range();
|
||||||
let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
|
let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
|
||||||
let mut builder = TextEditBuilder::default();
|
let mut builder = TextEditBuilder::default();
|
||||||
builder.replace(delete_range, snippet.to_string());
|
builder.replace(delete_range, snippet.to_string());
|
||||||
@ -38,9 +39,9 @@ fn is_bool_or_unknown(ty: Option<Ty>) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
if let Some(dot_receiver) = ctx.dot_receiver {
|
if let Some(dot_receiver) = &ctx.dot_receiver {
|
||||||
let receiver_text = dot_receiver.syntax().text().to_string();
|
let receiver_text = dot_receiver.syntax().text().to_string();
|
||||||
let receiver_ty = ctx.analyzer.type_of(ctx.db, dot_receiver);
|
let receiver_ty = ctx.analyzer.type_of(ctx.db, &dot_receiver);
|
||||||
if is_bool_or_unknown(receiver_ty) {
|
if is_bool_or_unknown(receiver_ty) {
|
||||||
postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
|
postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
|
||||||
.add_to(acc);
|
.add_to(acc);
|
||||||
|
@ -20,8 +20,8 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
|
|||||||
let mut builder = TextEditBuilder::default();
|
let mut builder = TextEditBuilder::default();
|
||||||
builder.replace(ctx.source_range(), name.to_string());
|
builder.replace(ctx.source_range(), name.to_string());
|
||||||
auto_import::auto_import_text_edit(
|
auto_import::auto_import_text_edit(
|
||||||
ctx.token.parent(),
|
&ctx.token.parent(),
|
||||||
ctx.token.parent(),
|
&ctx.token.parent(),
|
||||||
&path,
|
&path,
|
||||||
&mut builder,
|
&mut builder,
|
||||||
);
|
);
|
||||||
|
@ -4,8 +4,8 @@ use crate::completion::{CompletionContext, Completions};
|
|||||||
|
|
||||||
/// Complete fields in fields literals.
|
/// Complete fields in fields literals.
|
||||||
pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(super) fn complete_struct_literal(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
let (ty, variant) = match ctx.struct_lit_syntax.and_then(|it| {
|
let (ty, variant) = match ctx.struct_lit_syntax.as_ref().and_then(|it| {
|
||||||
Some((ctx.analyzer.type_of(ctx.db, it.into())?, ctx.analyzer.resolve_variant(it)?))
|
Some((ctx.analyzer.type_of(ctx.db, &it.clone().into())?, ctx.analyzer.resolve_variant(it)?))
|
||||||
}) {
|
}) {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
_ => return,
|
_ => return,
|
||||||
|
@ -16,11 +16,11 @@ pub(crate) struct CompletionContext<'a> {
|
|||||||
pub(super) db: &'a db::RootDatabase,
|
pub(super) db: &'a db::RootDatabase,
|
||||||
pub(super) analyzer: hir::SourceAnalyzer,
|
pub(super) analyzer: hir::SourceAnalyzer,
|
||||||
pub(super) offset: TextUnit,
|
pub(super) offset: TextUnit,
|
||||||
pub(super) token: SyntaxToken<'a>,
|
pub(super) token: SyntaxToken,
|
||||||
pub(super) module: Option<hir::Module>,
|
pub(super) module: Option<hir::Module>,
|
||||||
pub(super) function_syntax: Option<&'a ast::FnDef>,
|
pub(super) function_syntax: Option<ast::FnDef>,
|
||||||
pub(super) use_item_syntax: Option<&'a ast::UseItem>,
|
pub(super) use_item_syntax: Option<ast::UseItem>,
|
||||||
pub(super) struct_lit_syntax: Option<&'a ast::StructLit>,
|
pub(super) struct_lit_syntax: Option<ast::StructLit>,
|
||||||
pub(super) is_param: bool,
|
pub(super) is_param: bool,
|
||||||
/// If a name-binding or reference to a const in a pattern.
|
/// If a name-binding or reference to a const in a pattern.
|
||||||
/// Irrefutable patterns (like let) are excluded.
|
/// Irrefutable patterns (like let) are excluded.
|
||||||
@ -35,7 +35,7 @@ pub(crate) struct CompletionContext<'a> {
|
|||||||
/// Something is typed at the "top" level, in module or impl/trait.
|
/// Something is typed at the "top" level, in module or impl/trait.
|
||||||
pub(super) is_new_item: bool,
|
pub(super) is_new_item: bool,
|
||||||
/// The receiver if this is a field or method access, i.e. writing something.<|>
|
/// The receiver if this is a field or method access, i.e. writing something.<|>
|
||||||
pub(super) dot_receiver: Option<&'a ast::Expr>,
|
pub(super) dot_receiver: Option<ast::Expr>,
|
||||||
/// If this is a call (method or function) in particular, i.e. the () are already there.
|
/// If this is a call (method or function) in particular, i.e. the () are already there.
|
||||||
pub(super) is_call: bool,
|
pub(super) is_call: bool,
|
||||||
}
|
}
|
||||||
@ -50,7 +50,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
let token =
|
let token =
|
||||||
find_token_at_offset(original_parse.tree().syntax(), position.offset).left_biased()?;
|
find_token_at_offset(original_parse.tree().syntax(), position.offset).left_biased()?;
|
||||||
let analyzer =
|
let analyzer =
|
||||||
hir::SourceAnalyzer::new(db, position.file_id, token.parent(), Some(position.offset));
|
hir::SourceAnalyzer::new(db, position.file_id, &token.parent(), Some(position.offset));
|
||||||
let mut ctx = CompletionContext {
|
let mut ctx = CompletionContext {
|
||||||
db,
|
db,
|
||||||
analyzer,
|
analyzer,
|
||||||
@ -109,7 +109,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
if is_node::<ast::BindPat>(name.syntax()) {
|
if is_node::<ast::BindPat>(name.syntax()) {
|
||||||
let bind_pat = name.syntax().ancestors().find_map(ast::BindPat::cast).unwrap();
|
let bind_pat = name.syntax().ancestors().find_map(ast::BindPat::cast).unwrap();
|
||||||
let parent = bind_pat.syntax().parent();
|
let parent = bind_pat.syntax().parent();
|
||||||
if parent.and_then(ast::MatchArm::cast).is_some()
|
if parent.clone().and_then(ast::MatchArm::cast).is_some()
|
||||||
|| parent.and_then(ast::Condition::cast).is_some()
|
|| parent.and_then(ast::Condition::cast).is_some()
|
||||||
{
|
{
|
||||||
self.is_pat_binding = true;
|
self.is_pat_binding = true;
|
||||||
@ -122,7 +122,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn classify_name_ref(&mut self, original_file: &'a SourceFile, name_ref: &ast::NameRef) {
|
fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) {
|
||||||
let name_range = name_ref.syntax().range();
|
let name_range = name_ref.syntax().range();
|
||||||
if name_ref.syntax().parent().and_then(ast::NamedField::cast).is_some() {
|
if name_ref.syntax().parent().and_then(ast::NamedField::cast).is_some() {
|
||||||
self.struct_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset);
|
self.struct_lit_syntax = find_node_at_offset(original_file.syntax(), self.offset);
|
||||||
@ -153,7 +153,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(segment) = ast::PathSegment::cast(parent) {
|
if let Some(segment) = ast::PathSegment::cast(parent.clone()) {
|
||||||
let path = segment.parent_path();
|
let path = segment.parent_path();
|
||||||
self.is_call = path
|
self.is_call = path
|
||||||
.syntax()
|
.syntax()
|
||||||
@ -162,7 +162,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
.and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
|
.and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
|
||||||
.is_some();
|
.is_some();
|
||||||
|
|
||||||
if let Some(mut path) = hir::Path::from_ast(path) {
|
if let Some(mut path) = hir::Path::from_ast(path.clone()) {
|
||||||
if !path.is_ident() {
|
if !path.is_ident() {
|
||||||
path.segments.pop().unwrap();
|
path.segments.pop().unwrap();
|
||||||
self.path_prefix = Some(path);
|
self.path_prefix = Some(path);
|
||||||
@ -179,7 +179,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
.syntax()
|
.syntax()
|
||||||
.ancestors()
|
.ancestors()
|
||||||
.find_map(|node| {
|
.find_map(|node| {
|
||||||
if let Some(stmt) = ast::ExprStmt::cast(node) {
|
if let Some(stmt) = ast::ExprStmt::cast(node.clone()) {
|
||||||
return Some(stmt.syntax().range() == name_ref.syntax().range());
|
return Some(stmt.syntax().range() == name_ref.syntax().range());
|
||||||
}
|
}
|
||||||
if let Some(block) = ast::Block::cast(node) {
|
if let Some(block) = ast::Block::cast(node) {
|
||||||
@ -203,7 +203,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(field_expr) = ast::FieldExpr::cast(parent) {
|
if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
|
||||||
// The receiver comes before the point of insertion of the fake
|
// The receiver comes before the point of insertion of the fake
|
||||||
// ident, so it should have the same range in the non-modified file
|
// ident, so it should have the same range in the non-modified file
|
||||||
self.dot_receiver = field_expr
|
self.dot_receiver = field_expr
|
||||||
@ -222,7 +222,7 @@ impl<'a> CompletionContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<&N> {
|
fn find_node_with_range<N: AstNode>(syntax: &SyntaxNode, range: TextRange) -> Option<N> {
|
||||||
find_covering_element(syntax, range).ancestors().find_map(N::cast)
|
find_covering_element(syntax, range).ancestors().find_map(N::cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,8 +35,8 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
for node in parse.tree().syntax().descendants() {
|
for node in parse.tree().syntax().descendants() {
|
||||||
check_unnecessary_braces_in_use_statement(&mut res, file_id, node);
|
check_unnecessary_braces_in_use_statement(&mut res, file_id, &node);
|
||||||
check_struct_shorthand_initialization(&mut res, file_id, node);
|
check_struct_shorthand_initialization(&mut res, file_id, &node);
|
||||||
}
|
}
|
||||||
let res = RefCell::new(res);
|
let res = RefCell::new(res);
|
||||||
let mut sink = DiagnosticSink::new(|d| {
|
let mut sink = DiagnosticSink::new(|d| {
|
||||||
@ -60,7 +60,7 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
|
|||||||
})
|
})
|
||||||
.on::<hir::diagnostics::MissingFields, _>(|d| {
|
.on::<hir::diagnostics::MissingFields, _>(|d| {
|
||||||
let node = d.ast(db);
|
let node = d.ast(db);
|
||||||
let mut ast_editor = AstEditor::new(&*node);
|
let mut ast_editor = AstEditor::new(node);
|
||||||
for f in d.missed_fields.iter() {
|
for f in d.missed_fields.iter() {
|
||||||
ast_editor.append_field(&AstBuilder::<NamedField>::from_name(f));
|
ast_editor.append_field(&AstBuilder::<NamedField>::from_name(f));
|
||||||
}
|
}
|
||||||
@ -94,11 +94,11 @@ fn check_unnecessary_braces_in_use_statement(
|
|||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
node: &SyntaxNode,
|
node: &SyntaxNode,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let use_tree_list = ast::UseTreeList::cast(node)?;
|
let use_tree_list = ast::UseTreeList::cast(node.clone())?;
|
||||||
if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
|
if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
|
||||||
let range = use_tree_list.syntax().range();
|
let range = use_tree_list.syntax().range();
|
||||||
let edit =
|
let edit =
|
||||||
text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(single_use_tree)
|
text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
let to_replace = single_use_tree.syntax().text().to_string();
|
let to_replace = single_use_tree.syntax().text().to_string();
|
||||||
let mut edit_builder = TextEditBuilder::default();
|
let mut edit_builder = TextEditBuilder::default();
|
||||||
@ -141,7 +141,7 @@ fn check_struct_shorthand_initialization(
|
|||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
node: &SyntaxNode,
|
node: &SyntaxNode,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let struct_lit = ast::StructLit::cast(node)?;
|
let struct_lit = ast::StructLit::cast(node.clone())?;
|
||||||
let named_field_list = struct_lit.named_field_list()?;
|
let named_field_list = struct_lit.named_field_list()?;
|
||||||
for named_field in named_field_list.fields() {
|
for named_field in named_field_list.fields() {
|
||||||
if let (Some(name_ref), Some(expr)) = (named_field.name_ref(), named_field.expr()) {
|
if let (Some(name_ref), Some(expr)) = (named_field.name_ref(), named_field.expr()) {
|
||||||
@ -184,7 +184,7 @@ mod tests {
|
|||||||
let parse = SourceFile::parse(code);
|
let parse = SourceFile::parse(code);
|
||||||
let mut diagnostics = Vec::new();
|
let mut diagnostics = Vec::new();
|
||||||
for node in parse.tree().syntax().descendants() {
|
for node in parse.tree().syntax().descendants() {
|
||||||
func(&mut diagnostics, FileId(0), node);
|
func(&mut diagnostics, FileId(0), &node);
|
||||||
}
|
}
|
||||||
assert!(diagnostics.is_empty());
|
assert!(diagnostics.is_empty());
|
||||||
}
|
}
|
||||||
@ -193,7 +193,7 @@ mod tests {
|
|||||||
let parse = SourceFile::parse(before);
|
let parse = SourceFile::parse(before);
|
||||||
let mut diagnostics = Vec::new();
|
let mut diagnostics = Vec::new();
|
||||||
for node in parse.tree().syntax().descendants() {
|
for node in parse.tree().syntax().descendants() {
|
||||||
func(&mut diagnostics, FileId(0), node);
|
func(&mut diagnostics, FileId(0), &node);
|
||||||
}
|
}
|
||||||
let diagnostic =
|
let diagnostic =
|
||||||
diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
|
diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
|
||||||
|
@ -38,7 +38,7 @@ impl FunctionSignature {
|
|||||||
pub(crate) fn from_hir(db: &db::RootDatabase, function: hir::Function) -> Self {
|
pub(crate) fn from_hir(db: &db::RootDatabase, function: hir::Function) -> Self {
|
||||||
let doc = function.docs(db);
|
let doc = function.docs(db);
|
||||||
let ast_node = function.source(db).ast;
|
let ast_node = function.source(db).ast;
|
||||||
FunctionSignature::from(&*ast_node).with_doc_opt(doc)
|
FunctionSignature::from(&ast_node).with_doc_opt(doc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use ra_syntax::{
|
|||||||
ast::{self, DocCommentsOwner},
|
ast::{self, DocCommentsOwner},
|
||||||
AstNode, AstPtr, SmolStr,
|
AstNode, AstPtr, SmolStr,
|
||||||
SyntaxKind::{self, NAME},
|
SyntaxKind::{self, NAME},
|
||||||
SyntaxNode, TextRange, TreeArc,
|
SyntaxNode, TextRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::short_label::ShortLabel;
|
use super::short_label::ShortLabel;
|
||||||
@ -169,7 +169,7 @@ impl NavigationTarget {
|
|||||||
let file_id = src.file_id.original_file(db);
|
let file_id = src.file_id.original_file(db);
|
||||||
match src.ast {
|
match src.ast {
|
||||||
FieldSource::Named(it) => {
|
FieldSource::Named(it) => {
|
||||||
NavigationTarget::from_named(file_id, &*it, it.doc_comment_text(), it.short_label())
|
NavigationTarget::from_named(file_id, &it, it.doc_comment_text(), it.short_label())
|
||||||
}
|
}
|
||||||
FieldSource::Pos(it) => {
|
FieldSource::Pos(it) => {
|
||||||
NavigationTarget::from_syntax(file_id, "".into(), None, it.syntax(), None, None)
|
NavigationTarget::from_syntax(file_id, "".into(), None, it.syntax(), None, None)
|
||||||
@ -179,13 +179,13 @@ impl NavigationTarget {
|
|||||||
|
|
||||||
pub(crate) fn from_def_source<A, D>(db: &RootDatabase, def: D) -> NavigationTarget
|
pub(crate) fn from_def_source<A, D>(db: &RootDatabase, def: D) -> NavigationTarget
|
||||||
where
|
where
|
||||||
D: HasSource<Ast = TreeArc<A>>,
|
D: HasSource<Ast = A>,
|
||||||
A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
|
A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
|
||||||
{
|
{
|
||||||
let src = def.source(db);
|
let src = def.source(db);
|
||||||
NavigationTarget::from_named(
|
NavigationTarget::from_named(
|
||||||
src.file_id.original_file(db),
|
src.file_id.original_file(db),
|
||||||
&*src.ast,
|
&src.ast,
|
||||||
src.ast.doc_comment_text(),
|
src.ast.doc_comment_text(),
|
||||||
src.ast.short_label(),
|
src.ast.short_label(),
|
||||||
)
|
)
|
||||||
@ -249,7 +249,7 @@ impl NavigationTarget {
|
|||||||
log::debug!("nav target {}", src.ast.syntax().debug_dump());
|
log::debug!("nav target {}", src.ast.syntax().debug_dump());
|
||||||
NavigationTarget::from_named(
|
NavigationTarget::from_named(
|
||||||
src.file_id.original_file(db),
|
src.file_id.original_file(db),
|
||||||
&*src.ast,
|
&src.ast,
|
||||||
src.ast.doc_comment_text(),
|
src.ast.doc_comment_text(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@ -318,22 +318,18 @@ pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option
|
|||||||
let parse = db.parse(symbol.file_id);
|
let parse = db.parse(symbol.file_id);
|
||||||
let node = symbol.ptr.to_node(parse.tree().syntax()).to_owned();
|
let node = symbol.ptr.to_node(parse.tree().syntax()).to_owned();
|
||||||
|
|
||||||
fn doc_comments<N: ast::DocCommentsOwner>(node: &N) -> Option<String> {
|
|
||||||
node.doc_comment_text()
|
|
||||||
}
|
|
||||||
|
|
||||||
visitor()
|
visitor()
|
||||||
.visit(doc_comments::<ast::FnDef>)
|
.visit(|it: ast::FnDef| it.doc_comment_text())
|
||||||
.visit(doc_comments::<ast::StructDef>)
|
.visit(|it: ast::StructDef| it.doc_comment_text())
|
||||||
.visit(doc_comments::<ast::EnumDef>)
|
.visit(|it: ast::EnumDef| it.doc_comment_text())
|
||||||
.visit(doc_comments::<ast::TraitDef>)
|
.visit(|it: ast::TraitDef| it.doc_comment_text())
|
||||||
.visit(doc_comments::<ast::Module>)
|
.visit(|it: ast::Module| it.doc_comment_text())
|
||||||
.visit(doc_comments::<ast::TypeAliasDef>)
|
.visit(|it: ast::TypeAliasDef| it.doc_comment_text())
|
||||||
.visit(doc_comments::<ast::ConstDef>)
|
.visit(|it: ast::ConstDef| it.doc_comment_text())
|
||||||
.visit(doc_comments::<ast::StaticDef>)
|
.visit(|it: ast::StaticDef| it.doc_comment_text())
|
||||||
.visit(doc_comments::<ast::NamedFieldDef>)
|
.visit(|it: ast::NamedFieldDef| it.doc_comment_text())
|
||||||
.visit(doc_comments::<ast::EnumVariant>)
|
.visit(|it: ast::EnumVariant| it.doc_comment_text())
|
||||||
.visit(doc_comments::<ast::MacroCall>)
|
.visit(|it: ast::MacroCall| it.doc_comment_text())
|
||||||
.accept(&node)?
|
.accept(&node)?
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,15 +341,15 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) ->
|
|||||||
let node = symbol.ptr.to_node(parse.tree().syntax()).to_owned();
|
let node = symbol.ptr.to_node(parse.tree().syntax()).to_owned();
|
||||||
|
|
||||||
visitor()
|
visitor()
|
||||||
.visit(|node: &ast::FnDef| node.short_label())
|
.visit(|node: ast::FnDef| node.short_label())
|
||||||
.visit(|node: &ast::StructDef| node.short_label())
|
.visit(|node: ast::StructDef| node.short_label())
|
||||||
.visit(|node: &ast::EnumDef| node.short_label())
|
.visit(|node: ast::EnumDef| node.short_label())
|
||||||
.visit(|node: &ast::TraitDef| node.short_label())
|
.visit(|node: ast::TraitDef| node.short_label())
|
||||||
.visit(|node: &ast::Module| node.short_label())
|
.visit(|node: ast::Module| node.short_label())
|
||||||
.visit(|node: &ast::TypeAliasDef| node.short_label())
|
.visit(|node: ast::TypeAliasDef| node.short_label())
|
||||||
.visit(|node: &ast::ConstDef| node.short_label())
|
.visit(|node: ast::ConstDef| node.short_label())
|
||||||
.visit(|node: &ast::StaticDef| node.short_label())
|
.visit(|node: ast::StaticDef| node.short_label())
|
||||||
.visit(|node: &ast::NamedFieldDef| node.short_label())
|
.visit(|node: ast::NamedFieldDef| node.short_label())
|
||||||
.visit(|node: &ast::EnumVariant| node.short_label())
|
.visit(|node: ast::EnumVariant| node.short_label())
|
||||||
.accept(&node)?
|
.accept(&node)?
|
||||||
}
|
}
|
||||||
|
@ -24,14 +24,14 @@ pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
|
|||||||
for event in file.syntax().preorder() {
|
for event in file.syntax().preorder() {
|
||||||
match event {
|
match event {
|
||||||
WalkEvent::Enter(node) => {
|
WalkEvent::Enter(node) => {
|
||||||
if let Some(mut symbol) = structure_node(node) {
|
if let Some(mut symbol) = structure_node(&node) {
|
||||||
symbol.parent = stack.last().copied();
|
symbol.parent = stack.last().copied();
|
||||||
stack.push(res.len());
|
stack.push(res.len());
|
||||||
res.push(symbol);
|
res.push(symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WalkEvent::Leave(node) => {
|
WalkEvent::Leave(node) => {
|
||||||
if structure_node(node).is_some() {
|
if structure_node(&node).is_some() {
|
||||||
stack.pop().unwrap();
|
stack.pop().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,19 +41,20 @@ pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
|
fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
|
||||||
fn decl<N: NameOwner + AttrsOwner>(node: &N) -> Option<StructureNode> {
|
fn decl<N: NameOwner + AttrsOwner>(node: N) -> Option<StructureNode> {
|
||||||
decl_with_detail(node, None)
|
decl_with_detail(node, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decl_with_ascription<N: NameOwner + AttrsOwner + TypeAscriptionOwner>(
|
fn decl_with_ascription<N: NameOwner + AttrsOwner + TypeAscriptionOwner>(
|
||||||
node: &N,
|
node: N,
|
||||||
) -> Option<StructureNode> {
|
) -> Option<StructureNode> {
|
||||||
decl_with_type_ref(node, node.ascribed_type())
|
let ty = node.ascribed_type();
|
||||||
|
decl_with_type_ref(node, ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decl_with_type_ref<N: NameOwner + AttrsOwner>(
|
fn decl_with_type_ref<N: NameOwner + AttrsOwner>(
|
||||||
node: &N,
|
node: N,
|
||||||
type_ref: Option<&ast::TypeRef>,
|
type_ref: Option<ast::TypeRef>,
|
||||||
) -> Option<StructureNode> {
|
) -> Option<StructureNode> {
|
||||||
let detail = type_ref.map(|type_ref| {
|
let detail = type_ref.map(|type_ref| {
|
||||||
let mut detail = String::new();
|
let mut detail = String::new();
|
||||||
@ -64,7 +65,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn decl_with_detail<N: NameOwner + AttrsOwner>(
|
fn decl_with_detail<N: NameOwner + AttrsOwner>(
|
||||||
node: &N,
|
node: N,
|
||||||
detail: Option<String>,
|
detail: Option<String>,
|
||||||
) -> Option<StructureNode> {
|
) -> Option<StructureNode> {
|
||||||
let name = node.name()?;
|
let name = node.name()?;
|
||||||
@ -82,22 +83,24 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
|
|||||||
|
|
||||||
fn collapse_ws(node: &SyntaxNode, output: &mut String) {
|
fn collapse_ws(node: &SyntaxNode, output: &mut String) {
|
||||||
let mut can_insert_ws = false;
|
let mut can_insert_ws = false;
|
||||||
for line in node.text().chunks().flat_map(|chunk| chunk.lines()) {
|
for chunk in node.text().chunks() {
|
||||||
let line = line.trim();
|
for line in chunk.lines() {
|
||||||
if line.is_empty() {
|
let line = line.trim();
|
||||||
if can_insert_ws {
|
if line.is_empty() {
|
||||||
output.push_str(" ");
|
if can_insert_ws {
|
||||||
can_insert_ws = false;
|
output.push_str(" ");
|
||||||
|
can_insert_ws = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output.push_str(line);
|
||||||
|
can_insert_ws = true;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
output.push_str(line);
|
|
||||||
can_insert_ws = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitor()
|
visitor()
|
||||||
.visit(|fn_def: &ast::FnDef| {
|
.visit(|fn_def: ast::FnDef| {
|
||||||
let mut detail = String::from("fn");
|
let mut detail = String::from("fn");
|
||||||
if let Some(type_param_list) = fn_def.type_param_list() {
|
if let Some(type_param_list) = fn_def.type_param_list() {
|
||||||
collapse_ws(type_param_list.syntax(), &mut detail);
|
collapse_ws(type_param_list.syntax(), &mut detail);
|
||||||
@ -117,11 +120,14 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
|
|||||||
.visit(decl::<ast::EnumVariant>)
|
.visit(decl::<ast::EnumVariant>)
|
||||||
.visit(decl::<ast::TraitDef>)
|
.visit(decl::<ast::TraitDef>)
|
||||||
.visit(decl::<ast::Module>)
|
.visit(decl::<ast::Module>)
|
||||||
.visit(|td: &ast::TypeAliasDef| decl_with_type_ref(td, td.type_ref()))
|
.visit(|td: ast::TypeAliasDef| {
|
||||||
|
let ty = td.type_ref();
|
||||||
|
decl_with_type_ref(td, ty)
|
||||||
|
})
|
||||||
.visit(decl_with_ascription::<ast::NamedFieldDef>)
|
.visit(decl_with_ascription::<ast::NamedFieldDef>)
|
||||||
.visit(decl_with_ascription::<ast::ConstDef>)
|
.visit(decl_with_ascription::<ast::ConstDef>)
|
||||||
.visit(decl_with_ascription::<ast::StaticDef>)
|
.visit(decl_with_ascription::<ast::StaticDef>)
|
||||||
.visit(|im: &ast::ImplBlock| {
|
.visit(|im: ast::ImplBlock| {
|
||||||
let target_type = im.target_type()?;
|
let target_type = im.target_type()?;
|
||||||
let target_trait = im.target_trait();
|
let target_trait = im.target_trait();
|
||||||
let label = match target_trait {
|
let label = match target_trait {
|
||||||
@ -142,14 +148,14 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
|
|||||||
};
|
};
|
||||||
Some(node)
|
Some(node)
|
||||||
})
|
})
|
||||||
.visit(|mc: &ast::MacroCall| {
|
.visit(|mc: ast::MacroCall| {
|
||||||
let first_token = mc.syntax().first_token().unwrap();
|
let first_token = mc.syntax().first_token().unwrap();
|
||||||
if first_token.text().as_str() != "macro_rules" {
|
if first_token.text().as_str() != "macro_rules" {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
decl(mc)
|
decl(mc)
|
||||||
})
|
})
|
||||||
.accept(node)?
|
.accept(&node)?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -42,7 +42,7 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
|
|||||||
TokenAtOffset::None => return None,
|
TokenAtOffset::None => return None,
|
||||||
TokenAtOffset::Single(l) => {
|
TokenAtOffset::Single(l) => {
|
||||||
if string_kinds.contains(&l.kind()) {
|
if string_kinds.contains(&l.kind()) {
|
||||||
extend_single_word_in_comment_or_string(l, offset).unwrap_or_else(|| l.range())
|
extend_single_word_in_comment_or_string(&l, offset).unwrap_or_else(|| l.range())
|
||||||
} else {
|
} else {
|
||||||
l.range()
|
l.range()
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
|
|||||||
if token.range() != range {
|
if token.range() != range {
|
||||||
return Some(token.range());
|
return Some(token.range());
|
||||||
}
|
}
|
||||||
if let Some(comment) = ast::Comment::cast(token) {
|
if let Some(comment) = ast::Comment::cast(token.clone()) {
|
||||||
if let Some(range) = extend_comments(comment) {
|
if let Some(range) = extend_comments(comment) {
|
||||||
return Some(range);
|
return Some(range);
|
||||||
}
|
}
|
||||||
@ -73,7 +73,7 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
|
|||||||
let node = node.ancestors().take_while(|n| n.range() == node.range()).last().unwrap();
|
let node = node.ancestors().take_while(|n| n.range() == node.range()).last().unwrap();
|
||||||
|
|
||||||
if node.parent().map(|n| list_kinds.contains(&n.kind())) == Some(true) {
|
if node.parent().map(|n| list_kinds.contains(&n.kind())) == Some(true) {
|
||||||
if let Some(range) = extend_list_item(node) {
|
if let Some(range) = extend_list_item(&node) {
|
||||||
return Some(range);
|
return Some(range);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,7 +82,7 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn extend_single_word_in_comment_or_string(
|
fn extend_single_word_in_comment_or_string(
|
||||||
leaf: SyntaxToken,
|
leaf: &SyntaxToken,
|
||||||
offset: TextUnit,
|
offset: TextUnit,
|
||||||
) -> Option<TextRange> {
|
) -> Option<TextRange> {
|
||||||
let text: &str = leaf.text();
|
let text: &str = leaf.text();
|
||||||
@ -131,9 +131,9 @@ fn extend_ws(root: &SyntaxNode, ws: SyntaxToken, offset: TextUnit) -> TextRange
|
|||||||
ws.range()
|
ws.range()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pick_best<'a>(l: SyntaxToken<'a>, r: SyntaxToken<'a>) -> SyntaxToken<'a> {
|
fn pick_best<'a>(l: SyntaxToken, r: SyntaxToken) -> SyntaxToken {
|
||||||
return if priority(r) > priority(l) { r } else { l };
|
return if priority(&r) > priority(&l) { r } else { l };
|
||||||
fn priority(n: SyntaxToken) -> usize {
|
fn priority(n: &SyntaxToken) -> usize {
|
||||||
match n.kind() {
|
match n.kind() {
|
||||||
WHITESPACE => 0,
|
WHITESPACE => 0,
|
||||||
IDENT | T![self] | T![super] | T![crate] | LIFETIME => 2,
|
IDENT | T![self] | T![super] | T![crate] | LIFETIME => 2,
|
||||||
@ -156,7 +156,7 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> {
|
|||||||
SyntaxElement::Token(it) => is_single_line_ws(it),
|
SyntaxElement::Token(it) => is_single_line_ws(it),
|
||||||
})
|
})
|
||||||
.next()
|
.next()
|
||||||
.and_then(|it| it.as_token())
|
.and_then(|it| it.as_token().cloned())
|
||||||
.filter(|node| node.kind() == T![,])
|
.filter(|node| node.kind() == T![,])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> {
|
|||||||
// Include any following whitespace when comma if after list item.
|
// Include any following whitespace when comma if after list item.
|
||||||
let final_node = comma_node
|
let final_node = comma_node
|
||||||
.next_sibling_or_token()
|
.next_sibling_or_token()
|
||||||
.and_then(|it| it.as_token())
|
.and_then(|it| it.as_token().cloned())
|
||||||
.filter(|node| is_single_line_ws(node))
|
.filter(|node| is_single_line_ws(node))
|
||||||
.unwrap_or(comma_node);
|
.unwrap_or(comma_node);
|
||||||
|
|
||||||
@ -178,8 +178,8 @@ fn extend_list_item(node: &SyntaxNode) -> Option<TextRange> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn extend_comments(comment: ast::Comment) -> Option<TextRange> {
|
fn extend_comments(comment: ast::Comment) -> Option<TextRange> {
|
||||||
let prev = adj_comments(comment, Direction::Prev);
|
let prev = adj_comments(&comment, Direction::Prev);
|
||||||
let next = adj_comments(comment, Direction::Next);
|
let next = adj_comments(&comment, Direction::Next);
|
||||||
if prev != next {
|
if prev != next {
|
||||||
Some(TextRange::from_to(prev.syntax().range().start(), next.syntax().range().end()))
|
Some(TextRange::from_to(prev.syntax().range().start(), next.syntax().range().end()))
|
||||||
} else {
|
} else {
|
||||||
@ -187,14 +187,14 @@ fn extend_comments(comment: ast::Comment) -> Option<TextRange> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn adj_comments(comment: ast::Comment, dir: Direction) -> ast::Comment {
|
fn adj_comments(comment: &ast::Comment, dir: Direction) -> ast::Comment {
|
||||||
let mut res = comment;
|
let mut res = comment.clone();
|
||||||
for element in comment.syntax().siblings_with_tokens(dir) {
|
for element in comment.syntax().siblings_with_tokens(dir) {
|
||||||
let token = match element.as_token() {
|
let token = match element.as_token() {
|
||||||
None => break,
|
None => break,
|
||||||
Some(token) => token,
|
Some(token) => token,
|
||||||
};
|
};
|
||||||
if let Some(c) = ast::Comment::cast(token) {
|
if let Some(c) = ast::Comment::cast(token.clone()) {
|
||||||
res = c
|
res = c
|
||||||
} else if token.kind() != WHITESPACE || token.text().contains("\n\n") {
|
} else if token.kind() != WHITESPACE || token.text().contains("\n\n") {
|
||||||
break;
|
break;
|
||||||
|
@ -30,7 +30,7 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
|
|||||||
for element in file.syntax().descendants_with_tokens() {
|
for element in file.syntax().descendants_with_tokens() {
|
||||||
// Fold items that span multiple lines
|
// Fold items that span multiple lines
|
||||||
if let Some(kind) = fold_kind(element.kind()) {
|
if let Some(kind) = fold_kind(element.kind()) {
|
||||||
let is_multiline = match element {
|
let is_multiline = match &element {
|
||||||
SyntaxElement::Node(node) => node.text().contains('\n'),
|
SyntaxElement::Node(node) => node.text().contains('\n'),
|
||||||
SyntaxElement::Token(token) => token.text().contains('\n'),
|
SyntaxElement::Token(token) => token.text().contains('\n'),
|
||||||
};
|
};
|
||||||
@ -56,7 +56,7 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
|
|||||||
SyntaxElement::Node(node) => {
|
SyntaxElement::Node(node) => {
|
||||||
// Fold groups of imports
|
// Fold groups of imports
|
||||||
if node.kind() == USE_ITEM && !visited_imports.contains(&node) {
|
if node.kind() == USE_ITEM && !visited_imports.contains(&node) {
|
||||||
if let Some(range) = contiguous_range_for_group(node, &mut visited_imports) {
|
if let Some(range) = contiguous_range_for_group(&node, &mut visited_imports) {
|
||||||
res.push(Fold { range, kind: FoldKind::Imports })
|
res.push(Fold { range, kind: FoldKind::Imports })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -65,7 +65,7 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
|
|||||||
if node.kind() == MODULE && !has_visibility(&node) && !visited_mods.contains(&node)
|
if node.kind() == MODULE && !has_visibility(&node) && !visited_mods.contains(&node)
|
||||||
{
|
{
|
||||||
if let Some(range) =
|
if let Some(range) =
|
||||||
contiguous_range_for_group_unless(node, has_visibility, &mut visited_mods)
|
contiguous_range_for_group_unless(&node, has_visibility, &mut visited_mods)
|
||||||
{
|
{
|
||||||
res.push(Fold { range, kind: FoldKind::Mods })
|
res.push(Fold { range, kind: FoldKind::Mods })
|
||||||
}
|
}
|
||||||
@ -88,24 +88,24 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn has_visibility(node: &SyntaxNode) -> bool {
|
fn has_visibility(node: &SyntaxNode) -> bool {
|
||||||
ast::Module::cast(node).and_then(|m| m.visibility()).is_some()
|
ast::Module::cast(node.clone()).and_then(|m| m.visibility()).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contiguous_range_for_group<'a>(
|
fn contiguous_range_for_group(
|
||||||
first: &'a SyntaxNode,
|
first: &SyntaxNode,
|
||||||
visited: &mut FxHashSet<&'a SyntaxNode>,
|
visited: &mut FxHashSet<SyntaxNode>,
|
||||||
) -> Option<TextRange> {
|
) -> Option<TextRange> {
|
||||||
contiguous_range_for_group_unless(first, |_| false, visited)
|
contiguous_range_for_group_unless(first, |_| false, visited)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contiguous_range_for_group_unless<'a>(
|
fn contiguous_range_for_group_unless(
|
||||||
first: &'a SyntaxNode,
|
first: &SyntaxNode,
|
||||||
unless: impl Fn(&'a SyntaxNode) -> bool,
|
unless: impl Fn(&SyntaxNode) -> bool,
|
||||||
visited: &mut FxHashSet<&'a SyntaxNode>,
|
visited: &mut FxHashSet<SyntaxNode>,
|
||||||
) -> Option<TextRange> {
|
) -> Option<TextRange> {
|
||||||
visited.insert(first);
|
visited.insert(first.clone());
|
||||||
|
|
||||||
let mut last = first;
|
let mut last = first.clone();
|
||||||
for element in first.siblings_with_tokens(Direction::Next) {
|
for element in first.siblings_with_tokens(Direction::Next) {
|
||||||
let node = match element {
|
let node = match element {
|
||||||
SyntaxElement::Token(token) => {
|
SyntaxElement::Token(token) => {
|
||||||
@ -123,15 +123,15 @@ fn contiguous_range_for_group_unless<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Stop if we find a node that doesn't belong to the group
|
// Stop if we find a node that doesn't belong to the group
|
||||||
if node.kind() != first.kind() || unless(node) {
|
if node.kind() != first.kind() || unless(&node) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
visited.insert(node);
|
visited.insert(node.clone());
|
||||||
last = node;
|
last = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
if first != last {
|
if first != &last {
|
||||||
Some(TextRange::from_to(first.range().start(), last.range().end()))
|
Some(TextRange::from_to(first.range().start(), last.range().end()))
|
||||||
} else {
|
} else {
|
||||||
// The group consists of only one element, therefore it cannot be folded
|
// The group consists of only one element, therefore it cannot be folded
|
||||||
@ -139,11 +139,11 @@ fn contiguous_range_for_group_unless<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contiguous_range_for_comment<'a>(
|
fn contiguous_range_for_comment(
|
||||||
first: ast::Comment<'a>,
|
first: ast::Comment,
|
||||||
visited: &mut FxHashSet<ast::Comment<'a>>,
|
visited: &mut FxHashSet<ast::Comment>,
|
||||||
) -> Option<TextRange> {
|
) -> Option<TextRange> {
|
||||||
visited.insert(first);
|
visited.insert(first.clone());
|
||||||
|
|
||||||
// Only fold comments of the same flavor
|
// Only fold comments of the same flavor
|
||||||
let group_kind = first.kind();
|
let group_kind = first.kind();
|
||||||
@ -151,11 +151,11 @@ fn contiguous_range_for_comment<'a>(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut last = first;
|
let mut last = first.clone();
|
||||||
for element in first.syntax().siblings_with_tokens(Direction::Next) {
|
for element in first.syntax().siblings_with_tokens(Direction::Next) {
|
||||||
match element {
|
match element {
|
||||||
SyntaxElement::Token(token) => {
|
SyntaxElement::Token(token) => {
|
||||||
if let Some(ws) = ast::Whitespace::cast(token) {
|
if let Some(ws) = ast::Whitespace::cast(token.clone()) {
|
||||||
if !ws.spans_multiple_lines() {
|
if !ws.spans_multiple_lines() {
|
||||||
// Ignore whitespace without blank lines
|
// Ignore whitespace without blank lines
|
||||||
continue;
|
continue;
|
||||||
@ -163,7 +163,7 @@ fn contiguous_range_for_comment<'a>(
|
|||||||
}
|
}
|
||||||
if let Some(c) = ast::Comment::cast(token) {
|
if let Some(c) = ast::Comment::cast(token) {
|
||||||
if c.kind() == group_kind {
|
if c.kind() == group_kind {
|
||||||
visited.insert(c);
|
visited.insert(c.clone());
|
||||||
last = c;
|
last = c;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -193,7 +193,7 @@ mod tests {
|
|||||||
fn do_check(text: &str, fold_kinds: &[FoldKind]) {
|
fn do_check(text: &str, fold_kinds: &[FoldKind]) {
|
||||||
let (ranges, text) = extract_ranges(text, "fold");
|
let (ranges, text) = extract_ranges(text, "fold");
|
||||||
let parse = SourceFile::parse(&text);
|
let parse = SourceFile::parse(&text);
|
||||||
let folds = folding_ranges(parse.tree());
|
let folds = folding_ranges(&parse.tree());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
folds.len(),
|
folds.len(),
|
||||||
|
@ -20,13 +20,13 @@ pub(crate) fn goto_definition(
|
|||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
|
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
|
||||||
let parse = db.parse(position.file_id);
|
let parse = db.parse(position.file_id);
|
||||||
let syntax = parse.tree().syntax();
|
let syntax = parse.tree().syntax().clone();
|
||||||
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
|
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&syntax, position.offset) {
|
||||||
let navs = reference_definition(db, position.file_id, name_ref).to_vec();
|
let navs = reference_definition(db, position.file_id, &name_ref).to_vec();
|
||||||
return Some(RangeInfo::new(name_ref.syntax().range(), navs.to_vec()));
|
return Some(RangeInfo::new(name_ref.syntax().range(), navs.to_vec()));
|
||||||
}
|
}
|
||||||
if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
|
if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) {
|
||||||
let navs = name_definition(db, position.file_id, name)?;
|
let navs = name_definition(db, position.file_id, &name)?;
|
||||||
return Some(RangeInfo::new(name.syntax().range(), navs));
|
return Some(RangeInfo::new(name.syntax().range(), navs));
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
@ -94,7 +94,7 @@ pub(crate) fn name_definition(
|
|||||||
) -> Option<Vec<NavigationTarget>> {
|
) -> Option<Vec<NavigationTarget>> {
|
||||||
let parent = name.syntax().parent()?;
|
let parent = name.syntax().parent()?;
|
||||||
|
|
||||||
if let Some(module) = ast::Module::cast(&parent) {
|
if let Some(module) = ast::Module::cast(parent.clone()) {
|
||||||
if module.has_semi() {
|
if module.has_semi() {
|
||||||
if let Some(child_module) =
|
if let Some(child_module) =
|
||||||
hir::source_binder::module_from_declaration(db, file_id, module)
|
hir::source_binder::module_from_declaration(db, file_id, module)
|
||||||
@ -114,38 +114,88 @@ pub(crate) fn name_definition(
|
|||||||
|
|
||||||
fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> {
|
fn named_target(file_id: FileId, node: &SyntaxNode) -> Option<NavigationTarget> {
|
||||||
visitor()
|
visitor()
|
||||||
.visit(|node: &ast::StructDef| {
|
.visit(|node: ast::StructDef| {
|
||||||
NavigationTarget::from_named(file_id, node, node.doc_comment_text(), node.short_label())
|
NavigationTarget::from_named(
|
||||||
|
file_id,
|
||||||
|
&node,
|
||||||
|
node.doc_comment_text(),
|
||||||
|
node.short_label(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::EnumDef| {
|
.visit(|node: ast::EnumDef| {
|
||||||
NavigationTarget::from_named(file_id, node, node.doc_comment_text(), node.short_label())
|
NavigationTarget::from_named(
|
||||||
|
file_id,
|
||||||
|
&node,
|
||||||
|
node.doc_comment_text(),
|
||||||
|
node.short_label(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::EnumVariant| {
|
.visit(|node: ast::EnumVariant| {
|
||||||
NavigationTarget::from_named(file_id, node, node.doc_comment_text(), node.short_label())
|
NavigationTarget::from_named(
|
||||||
|
file_id,
|
||||||
|
&node,
|
||||||
|
node.doc_comment_text(),
|
||||||
|
node.short_label(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::FnDef| {
|
.visit(|node: ast::FnDef| {
|
||||||
NavigationTarget::from_named(file_id, node, node.doc_comment_text(), node.short_label())
|
NavigationTarget::from_named(
|
||||||
|
file_id,
|
||||||
|
&node,
|
||||||
|
node.doc_comment_text(),
|
||||||
|
node.short_label(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::TypeAliasDef| {
|
.visit(|node: ast::TypeAliasDef| {
|
||||||
NavigationTarget::from_named(file_id, node, node.doc_comment_text(), node.short_label())
|
NavigationTarget::from_named(
|
||||||
|
file_id,
|
||||||
|
&node,
|
||||||
|
node.doc_comment_text(),
|
||||||
|
node.short_label(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::ConstDef| {
|
.visit(|node: ast::ConstDef| {
|
||||||
NavigationTarget::from_named(file_id, node, node.doc_comment_text(), node.short_label())
|
NavigationTarget::from_named(
|
||||||
|
file_id,
|
||||||
|
&node,
|
||||||
|
node.doc_comment_text(),
|
||||||
|
node.short_label(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::StaticDef| {
|
.visit(|node: ast::StaticDef| {
|
||||||
NavigationTarget::from_named(file_id, node, node.doc_comment_text(), node.short_label())
|
NavigationTarget::from_named(
|
||||||
|
file_id,
|
||||||
|
&node,
|
||||||
|
node.doc_comment_text(),
|
||||||
|
node.short_label(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::TraitDef| {
|
.visit(|node: ast::TraitDef| {
|
||||||
NavigationTarget::from_named(file_id, node, node.doc_comment_text(), node.short_label())
|
NavigationTarget::from_named(
|
||||||
|
file_id,
|
||||||
|
&node,
|
||||||
|
node.doc_comment_text(),
|
||||||
|
node.short_label(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::NamedFieldDef| {
|
.visit(|node: ast::NamedFieldDef| {
|
||||||
NavigationTarget::from_named(file_id, node, node.doc_comment_text(), node.short_label())
|
NavigationTarget::from_named(
|
||||||
|
file_id,
|
||||||
|
&node,
|
||||||
|
node.doc_comment_text(),
|
||||||
|
node.short_label(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::Module| {
|
.visit(|node: ast::Module| {
|
||||||
NavigationTarget::from_named(file_id, node, node.doc_comment_text(), node.short_label())
|
NavigationTarget::from_named(
|
||||||
|
file_id,
|
||||||
|
&node,
|
||||||
|
node.doc_comment_text(),
|
||||||
|
node.short_label(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::MacroCall| {
|
.visit(|node: ast::MacroCall| {
|
||||||
NavigationTarget::from_named(file_id, node, node.doc_comment_text(), None)
|
NavigationTarget::from_named(file_id, &node, node.doc_comment_text(), None)
|
||||||
})
|
})
|
||||||
.accept(node)
|
.accept(node)
|
||||||
}
|
}
|
||||||
|
@ -13,15 +13,17 @@ pub(crate) fn goto_type_definition(
|
|||||||
token
|
token
|
||||||
.parent()
|
.parent()
|
||||||
.ancestors()
|
.ancestors()
|
||||||
.find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some())
|
.find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let analyzer = hir::SourceAnalyzer::new(db, position.file_id, node, None);
|
let analyzer = hir::SourceAnalyzer::new(db, position.file_id, &node, None);
|
||||||
|
|
||||||
let ty: hir::Ty = if let Some(ty) = ast::Expr::cast(node).and_then(|e| analyzer.type_of(db, e))
|
let ty: hir::Ty = if let Some(ty) =
|
||||||
|
ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e))
|
||||||
{
|
{
|
||||||
ty
|
ty
|
||||||
} else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, p)) {
|
} else if let Some(ty) = ast::Pat::cast(node.clone()).and_then(|p| analyzer.type_of_pat(db, &p))
|
||||||
|
{
|
||||||
ty
|
ty
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
|
@ -6,7 +6,7 @@ use ra_syntax::{
|
|||||||
visit::{visitor, Visitor},
|
visit::{visitor, Visitor},
|
||||||
},
|
},
|
||||||
ast::{self, DocCommentsOwner},
|
ast::{self, DocCommentsOwner},
|
||||||
AstNode, TreeArc,
|
AstNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -104,7 +104,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
|
|||||||
|
|
||||||
let mut no_fallback = false;
|
let mut no_fallback = false;
|
||||||
|
|
||||||
match classify_name_ref(db, &analyzer, name_ref) {
|
match classify_name_ref(db, &analyzer, &name_ref) {
|
||||||
Some(Method(it)) => res.extend(from_def_source(db, it)),
|
Some(Method(it)) => res.extend(from_def_source(db, it)),
|
||||||
Some(Macro(it)) => {
|
Some(Macro(it)) => {
|
||||||
let src = it.source(db);
|
let src = it.source(db);
|
||||||
@ -163,7 +163,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
|
|||||||
|
|
||||||
if res.is_empty() && !no_fallback {
|
if res.is_empty() && !no_fallback {
|
||||||
// Fallback index based approach:
|
// Fallback index based approach:
|
||||||
let symbols = crate::symbol_index::index_resolve(db, name_ref);
|
let symbols = crate::symbol_index::index_resolve(db, &name_ref);
|
||||||
for sym in symbols {
|
for sym in symbols {
|
||||||
let docs = docs_from_symbol(db, &sym);
|
let docs = docs_from_symbol(db, &sym);
|
||||||
let desc = description_from_symbol(db, &sym);
|
let desc = description_from_symbol(db, &sym);
|
||||||
@ -177,34 +177,32 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
|
|||||||
} else if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
|
} else if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
|
||||||
if let Some(parent) = name.syntax().parent() {
|
if let Some(parent) = name.syntax().parent() {
|
||||||
let text = visitor()
|
let text = visitor()
|
||||||
.visit(|node: &ast::StructDef| {
|
.visit(|node: ast::StructDef| {
|
||||||
hover_text(node.doc_comment_text(), node.short_label())
|
hover_text(node.doc_comment_text(), node.short_label())
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::EnumDef| {
|
.visit(|node: ast::EnumDef| hover_text(node.doc_comment_text(), node.short_label()))
|
||||||
|
.visit(|node: ast::EnumVariant| {
|
||||||
hover_text(node.doc_comment_text(), node.short_label())
|
hover_text(node.doc_comment_text(), node.short_label())
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::EnumVariant| {
|
.visit(|node: ast::FnDef| hover_text(node.doc_comment_text(), node.short_label()))
|
||||||
|
.visit(|node: ast::TypeAliasDef| {
|
||||||
hover_text(node.doc_comment_text(), node.short_label())
|
hover_text(node.doc_comment_text(), node.short_label())
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::FnDef| hover_text(node.doc_comment_text(), node.short_label()))
|
.visit(|node: ast::ConstDef| {
|
||||||
.visit(|node: &ast::TypeAliasDef| {
|
|
||||||
hover_text(node.doc_comment_text(), node.short_label())
|
hover_text(node.doc_comment_text(), node.short_label())
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::ConstDef| {
|
.visit(|node: ast::StaticDef| {
|
||||||
hover_text(node.doc_comment_text(), node.short_label())
|
hover_text(node.doc_comment_text(), node.short_label())
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::StaticDef| {
|
.visit(|node: ast::TraitDef| {
|
||||||
hover_text(node.doc_comment_text(), node.short_label())
|
hover_text(node.doc_comment_text(), node.short_label())
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::TraitDef| {
|
.visit(|node: ast::NamedFieldDef| {
|
||||||
hover_text(node.doc_comment_text(), node.short_label())
|
hover_text(node.doc_comment_text(), node.short_label())
|
||||||
})
|
})
|
||||||
.visit(|node: &ast::NamedFieldDef| {
|
.visit(|node: ast::Module| hover_text(node.doc_comment_text(), node.short_label()))
|
||||||
hover_text(node.doc_comment_text(), node.short_label())
|
.visit(|node: ast::MacroCall| hover_text(node.doc_comment_text(), None))
|
||||||
})
|
.accept(&parent);
|
||||||
.visit(|node: &ast::Module| hover_text(node.doc_comment_text(), node.short_label()))
|
|
||||||
.visit(|node: &ast::MacroCall| hover_text(node.doc_comment_text(), None))
|
|
||||||
.accept(parent);
|
|
||||||
|
|
||||||
if let Some(text) = text {
|
if let Some(text) = text {
|
||||||
res.extend(text);
|
res.extend(text);
|
||||||
@ -217,8 +215,9 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
if range.is_none() {
|
if range.is_none() {
|
||||||
let node = ancestors_at_offset(file.syntax(), position.offset)
|
let node = ancestors_at_offset(file.syntax(), position.offset).find(|n| {
|
||||||
.find(|n| ast::Expr::cast(*n).is_some() || ast::Pat::cast(*n).is_some())?;
|
ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some()
|
||||||
|
})?;
|
||||||
let frange = FileRange { file_id: position.file_id, range: node.range() };
|
let frange = FileRange { file_id: position.file_id, range: node.range() };
|
||||||
res.extend(type_of(db, frange).map(rust_code_markup));
|
res.extend(type_of(db, frange).map(rust_code_markup));
|
||||||
range = Some(node.range());
|
range = Some(node.range());
|
||||||
@ -233,7 +232,7 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
|
|||||||
|
|
||||||
fn from_def_source<A, D>(db: &RootDatabase, def: D) -> Option<String>
|
fn from_def_source<A, D>(db: &RootDatabase, def: D) -> Option<String>
|
||||||
where
|
where
|
||||||
D: HasSource<Ast = TreeArc<A>>,
|
D: HasSource<Ast = A>,
|
||||||
A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
|
A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
|
||||||
{
|
{
|
||||||
let src = def.source(db);
|
let src = def.source(db);
|
||||||
@ -243,17 +242,17 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
|
|||||||
|
|
||||||
pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
|
pub(crate) fn type_of(db: &RootDatabase, frange: FileRange) -> Option<String> {
|
||||||
let parse = db.parse(frange.file_id);
|
let parse = db.parse(frange.file_id);
|
||||||
let syntax = parse.tree().syntax();
|
let leaf_node = find_covering_element(parse.tree().syntax(), frange.range);
|
||||||
let leaf_node = find_covering_element(syntax, frange.range);
|
|
||||||
// if we picked identifier, expand to pattern/expression
|
// if we picked identifier, expand to pattern/expression
|
||||||
let node = leaf_node
|
let node = leaf_node
|
||||||
.ancestors()
|
.ancestors()
|
||||||
.take_while(|it| it.range() == leaf_node.range())
|
.take_while(|it| it.range() == leaf_node.range())
|
||||||
.find(|&it| ast::Expr::cast(it).is_some() || ast::Pat::cast(it).is_some())?;
|
.find(|it| ast::Expr::cast(it.clone()).is_some() || ast::Pat::cast(it.clone()).is_some())?;
|
||||||
let analyzer = hir::SourceAnalyzer::new(db, frange.file_id, node, None);
|
let analyzer = hir::SourceAnalyzer::new(db, frange.file_id, &node, None);
|
||||||
let ty = if let Some(ty) = ast::Expr::cast(node).and_then(|e| analyzer.type_of(db, e)) {
|
let ty = if let Some(ty) = ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e))
|
||||||
|
{
|
||||||
ty
|
ty
|
||||||
} else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, p)) {
|
} else if let Some(ty) = ast::Pat::cast(node).and_then(|p| analyzer.type_of_pat(db, &p)) {
|
||||||
ty
|
ty
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
|
@ -9,19 +9,19 @@ pub(crate) fn goto_implementation(
|
|||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
|
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
|
||||||
let parse = db.parse(position.file_id);
|
let parse = db.parse(position.file_id);
|
||||||
let syntax = parse.tree().syntax();
|
let syntax = parse.tree().syntax().clone();
|
||||||
|
|
||||||
let module = source_binder::module_from_position(db, position)?;
|
let module = source_binder::module_from_position(db, position)?;
|
||||||
|
|
||||||
if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(syntax, position.offset) {
|
if let Some(nominal_def) = find_node_at_offset::<ast::NominalDef>(&syntax, position.offset) {
|
||||||
return Some(RangeInfo::new(
|
return Some(RangeInfo::new(
|
||||||
nominal_def.syntax().range(),
|
nominal_def.syntax().range(),
|
||||||
impls_for_def(db, nominal_def, module)?,
|
impls_for_def(db, &nominal_def, module)?,
|
||||||
));
|
));
|
||||||
} else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(syntax, position.offset) {
|
} else if let Some(trait_def) = find_node_at_offset::<ast::TraitDef>(&syntax, position.offset) {
|
||||||
return Some(RangeInfo::new(
|
return Some(RangeInfo::new(
|
||||||
trait_def.syntax().range(),
|
trait_def.syntax().range(),
|
||||||
impls_for_trait(db, trait_def, module)?,
|
impls_for_trait(db, &trait_def, module)?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
|
|||||||
SyntaxElement::Token(token) => token.parent(),
|
SyntaxElement::Token(token) => token.parent(),
|
||||||
};
|
};
|
||||||
let mut edit = TextEditBuilder::default();
|
let mut edit = TextEditBuilder::default();
|
||||||
for token in node.descendants_with_tokens().filter_map(|it| it.as_token()) {
|
for token in node.descendants_with_tokens().filter_map(|it| it.as_token().cloned()) {
|
||||||
let range = match range.intersection(&token.range()) {
|
let range = match range.intersection(&token.range()) {
|
||||||
Some(range) => range,
|
Some(range) => range,
|
||||||
None => continue,
|
None => continue,
|
||||||
@ -37,7 +37,7 @@ pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
|
|||||||
let pos: TextUnit = (pos as u32).into();
|
let pos: TextUnit = (pos as u32).into();
|
||||||
let off = token.range().start() + range.start() + pos;
|
let off = token.range().start() + range.start() + pos;
|
||||||
if !edit.invalidates_offset(off) {
|
if !edit.invalidates_offset(off) {
|
||||||
remove_newline(&mut edit, token, off);
|
remove_newline(&mut edit, &token, off);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ pub fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
|
|||||||
edit.finish()
|
edit.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_newline(edit: &mut TextEditBuilder, token: SyntaxToken, offset: TextUnit) {
|
fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextUnit) {
|
||||||
if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 {
|
if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 {
|
||||||
// The node is either the first or the last in the file
|
// The node is either the first or the last in the file
|
||||||
let suff = &token.text()[TextRange::from_to(
|
let suff = &token.text()[TextRange::from_to(
|
||||||
@ -98,9 +98,10 @@ fn remove_newline(edit: &mut TextEditBuilder, token: SyntaxToken, offset: TextUn
|
|||||||
TextRange::from_to(prev.range().start(), token.range().end()),
|
TextRange::from_to(prev.range().start(), token.range().end()),
|
||||||
space.to_string(),
|
space.to_string(),
|
||||||
);
|
);
|
||||||
} else if let (Some(_), Some(next)) =
|
} else if let (Some(_), Some(next)) = (
|
||||||
(prev.as_token().and_then(ast::Comment::cast), next.as_token().and_then(ast::Comment::cast))
|
prev.as_token().cloned().and_then(ast::Comment::cast),
|
||||||
{
|
next.as_token().cloned().and_then(ast::Comment::cast),
|
||||||
|
) {
|
||||||
// Removes: newline (incl. surrounding whitespace), start of the next comment
|
// Removes: newline (incl. surrounding whitespace), start of the next comment
|
||||||
edit.delete(TextRange::from_to(
|
edit.delete(TextRange::from_to(
|
||||||
token.range().start(),
|
token.range().start(),
|
||||||
@ -113,16 +114,16 @@ fn remove_newline(edit: &mut TextEditBuilder, token: SyntaxToken, offset: TextUn
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn has_comma_after(node: &SyntaxNode) -> bool {
|
fn has_comma_after(node: &SyntaxNode) -> bool {
|
||||||
match non_trivia_sibling(node.into(), Direction::Next) {
|
match non_trivia_sibling(node.clone().into(), Direction::Next) {
|
||||||
Some(n) => n.kind() == T![,],
|
Some(n) => n.kind() == T![,],
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn join_single_expr_block(edit: &mut TextEditBuilder, token: SyntaxToken) -> Option<()> {
|
fn join_single_expr_block(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Option<()> {
|
||||||
let block = ast::Block::cast(token.parent())?;
|
let block = ast::Block::cast(token.parent())?;
|
||||||
let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?;
|
let block_expr = ast::BlockExpr::cast(block.syntax().parent()?)?;
|
||||||
let expr = extract_trivial_expression(block)?;
|
let expr = extract_trivial_expression(&block)?;
|
||||||
|
|
||||||
let block_range = block_expr.syntax().range();
|
let block_range = block_expr.syntax().range();
|
||||||
let mut buf = expr.syntax().text().to_string();
|
let mut buf = expr.syntax().text().to_string();
|
||||||
@ -139,7 +140,7 @@ fn join_single_expr_block(edit: &mut TextEditBuilder, token: SyntaxToken) -> Opt
|
|||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn join_single_use_tree(edit: &mut TextEditBuilder, token: SyntaxToken) -> Option<()> {
|
fn join_single_use_tree(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Option<()> {
|
||||||
let use_tree_list = ast::UseTreeList::cast(token.parent())?;
|
let use_tree_list = ast::UseTreeList::cast(token.parent())?;
|
||||||
let (tree,) = use_tree_list.use_trees().collect_tuple()?;
|
let (tree,) = use_tree_list.use_trees().collect_tuple()?;
|
||||||
edit.replace(use_tree_list.syntax().range(), tree.syntax().text().to_string());
|
edit.replace(use_tree_list.syntax().range(), tree.syntax().text().to_string());
|
||||||
@ -504,7 +505,7 @@ fn foo() {
|
|||||||
fn check_join_lines_sel(before: &str, after: &str) {
|
fn check_join_lines_sel(before: &str, after: &str) {
|
||||||
let (sel, before) = extract_range(before);
|
let (sel, before) = extract_range(before);
|
||||||
let parse = SourceFile::parse(&before);
|
let parse = SourceFile::parse(&before);
|
||||||
let result = join_lines(parse.tree(), sel);
|
let result = join_lines(&parse.tree(), sel);
|
||||||
let actual = result.apply(&before);
|
let actual = result.apply(&before);
|
||||||
assert_eq_text!(after, &actual);
|
assert_eq_text!(after, &actual);
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ use ra_db::{
|
|||||||
salsa::{self, ParallelDatabase},
|
salsa::{self, ParallelDatabase},
|
||||||
CheckCanceled, SourceDatabase,
|
CheckCanceled, SourceDatabase,
|
||||||
};
|
};
|
||||||
use ra_syntax::{SourceFile, TextRange, TextUnit, TreeArc};
|
use ra_syntax::{SourceFile, TextRange, TextUnit};
|
||||||
use ra_text_edit::TextEdit;
|
use ra_text_edit::TextEdit;
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
|
|
||||||
@ -325,8 +325,8 @@ impl Analysis {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the syntax tree of the file.
|
/// Gets the syntax tree of the file.
|
||||||
pub fn parse(&self, file_id: FileId) -> TreeArc<SourceFile> {
|
pub fn parse(&self, file_id: FileId) -> SourceFile {
|
||||||
self.db.parse(file_id).tree().to_owned()
|
self.db.parse(file_id).tree()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the file's `LineIndex`: data structure to convert between absolute
|
/// Gets the file's `LineIndex`: data structure to convert between absolute
|
||||||
@ -360,7 +360,7 @@ impl Analysis {
|
|||||||
let parse = self.db.parse(frange.file_id);
|
let parse = self.db.parse(frange.file_id);
|
||||||
let file_edit = SourceFileEdit {
|
let file_edit = SourceFileEdit {
|
||||||
file_id: frange.file_id,
|
file_id: frange.file_id,
|
||||||
edit: join_lines::join_lines(parse.tree(), frange.range),
|
edit: join_lines::join_lines(&parse.tree(), frange.range),
|
||||||
};
|
};
|
||||||
SourceChange::source_file_edit("join lines", file_edit)
|
SourceChange::source_file_edit("join lines", file_edit)
|
||||||
}
|
}
|
||||||
@ -393,13 +393,13 @@ impl Analysis {
|
|||||||
/// file outline.
|
/// file outline.
|
||||||
pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
|
pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
|
||||||
let parse = self.db.parse(file_id);
|
let parse = self.db.parse(file_id);
|
||||||
file_structure(parse.tree())
|
file_structure(&parse.tree())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the set of folding ranges.
|
/// Returns the set of folding ranges.
|
||||||
pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> {
|
pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> {
|
||||||
let parse = self.db.parse(file_id);
|
let parse = self.db.parse(file_id);
|
||||||
folding_ranges::folding_ranges(parse.tree())
|
folding_ranges::folding_ranges(&parse.tree())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fuzzy searches for a symbol.
|
/// Fuzzy searches for a symbol.
|
||||||
|
@ -26,7 +26,7 @@ mod tests {
|
|||||||
fn do_check(before: &str, after: &str) {
|
fn do_check(before: &str, after: &str) {
|
||||||
let (pos, before) = extract_offset(before);
|
let (pos, before) = extract_offset(before);
|
||||||
let parse = SourceFile::parse(&before);
|
let parse = SourceFile::parse(&before);
|
||||||
let new_pos = match matching_brace(parse.tree(), pos) {
|
let new_pos = match matching_brace(&parse.tree(), pos) {
|
||||||
None => pos,
|
None => pos,
|
||||||
Some(pos) => pos,
|
Some(pos) => pos,
|
||||||
};
|
};
|
||||||
|
@ -26,7 +26,7 @@ pub(crate) fn classify_name_ref(
|
|||||||
// Check if it is a method
|
// Check if it is a method
|
||||||
if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) {
|
if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) {
|
||||||
tested_by!(goto_definition_works_for_methods);
|
tested_by!(goto_definition_works_for_methods);
|
||||||
if let Some(func) = analyzer.resolve_method_call(method_call) {
|
if let Some(func) = analyzer.resolve_method_call(&method_call) {
|
||||||
return Some(Method(func));
|
return Some(Method(func));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,7 +40,7 @@ pub(crate) fn classify_name_ref(
|
|||||||
.and_then(ast::MacroCall::cast)
|
.and_then(ast::MacroCall::cast)
|
||||||
{
|
{
|
||||||
tested_by!(goto_definition_works_for_macros);
|
tested_by!(goto_definition_works_for_macros);
|
||||||
if let Some(mac) = analyzer.resolve_macro_call(db, macro_call) {
|
if let Some(mac) = analyzer.resolve_macro_call(db, ¯o_call) {
|
||||||
return Some(Macro(mac));
|
return Some(Macro(mac));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -48,7 +48,7 @@ pub(crate) fn classify_name_ref(
|
|||||||
// It could also be a field access
|
// It could also be a field access
|
||||||
if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) {
|
if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) {
|
||||||
tested_by!(goto_definition_works_for_fields);
|
tested_by!(goto_definition_works_for_fields);
|
||||||
if let Some(field) = analyzer.resolve_field(field_expr) {
|
if let Some(field) = analyzer.resolve_field(&field_expr) {
|
||||||
return Some(FieldAccess(field));
|
return Some(FieldAccess(field));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ pub(crate) fn classify_name_ref(
|
|||||||
|
|
||||||
let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast);
|
let struct_lit = field_expr.syntax().ancestors().find_map(ast::StructLit::cast);
|
||||||
|
|
||||||
if let Some(ty) = struct_lit.and_then(|lit| analyzer.type_of(db, lit.into())) {
|
if let Some(ty) = struct_lit.and_then(|lit| analyzer.type_of(db, &lit.into())) {
|
||||||
if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() {
|
if let Some((hir::AdtDef::Struct(s), _)) = ty.as_adt() {
|
||||||
let hir_path = hir::Path::from_name_ref(name_ref);
|
let hir_path = hir::Path::from_name_ref(name_ref);
|
||||||
let hir_name = hir_path.as_ident().unwrap();
|
let hir_name = hir_path.as_ident().unwrap();
|
||||||
@ -73,7 +73,7 @@ pub(crate) fn classify_name_ref(
|
|||||||
|
|
||||||
// General case, a path or a local:
|
// General case, a path or a local:
|
||||||
if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
|
if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
|
||||||
if let Some(resolved) = analyzer.resolve_path(db, path) {
|
if let Some(resolved) = analyzer.resolve_path(db, &path) {
|
||||||
return match resolved {
|
return match resolved {
|
||||||
hir::PathResolution::Def(def) => Some(Def(def)),
|
hir::PathResolution::Def(def) => Some(Def(def)),
|
||||||
hir::PathResolution::LocalBinding(Either::A(pat)) => Some(Pat(pat)),
|
hir::PathResolution::LocalBinding(Either::A(pat)) => Some(Pat(pat)),
|
||||||
|
@ -50,11 +50,11 @@ pub(crate) fn find_all_refs(
|
|||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
) -> Option<ReferenceSearchResult> {
|
) -> Option<ReferenceSearchResult> {
|
||||||
let parse = db.parse(position.file_id);
|
let parse = db.parse(position.file_id);
|
||||||
let (binding, analyzer) = find_binding(db, parse.tree(), position)?;
|
let (binding, analyzer) = find_binding(db, &parse.tree(), position)?;
|
||||||
let declaration = NavigationTarget::from_bind_pat(position.file_id, binding);
|
let declaration = NavigationTarget::from_bind_pat(position.file_id, &binding);
|
||||||
|
|
||||||
let references = analyzer
|
let references = analyzer
|
||||||
.find_all_refs(binding)
|
.find_all_refs(&binding)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range })
|
.map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range })
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
@ -63,9 +63,9 @@ pub(crate) fn find_all_refs(
|
|||||||
|
|
||||||
fn find_binding<'a>(
|
fn find_binding<'a>(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
source_file: &'a SourceFile,
|
source_file: &SourceFile,
|
||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
) -> Option<(&'a ast::BindPat, hir::SourceAnalyzer)> {
|
) -> Option<(ast::BindPat, hir::SourceAnalyzer)> {
|
||||||
let syntax = source_file.syntax();
|
let syntax = source_file.syntax();
|
||||||
if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) {
|
if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) {
|
||||||
let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None);
|
let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None);
|
||||||
@ -73,7 +73,7 @@ pub(crate) fn find_all_refs(
|
|||||||
};
|
};
|
||||||
let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
|
let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
|
||||||
let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
|
let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None);
|
||||||
let resolved = analyzer.resolve_local_name(name_ref)?;
|
let resolved = analyzer.resolve_local_name(&name_ref)?;
|
||||||
if let Either::A(ptr) = resolved.ptr() {
|
if let Either::A(ptr) = resolved.ptr() {
|
||||||
if let ast::PatKind::BindPat(binding) = ptr.to_node(source_file.syntax()).kind() {
|
if let ast::PatKind::BindPat(binding) = ptr.to_node(source_file.syntax()).kind() {
|
||||||
return Some((binding, analyzer));
|
return Some((binding, analyzer));
|
||||||
@ -89,10 +89,10 @@ pub(crate) fn rename(
|
|||||||
new_name: &str,
|
new_name: &str,
|
||||||
) -> Option<SourceChange> {
|
) -> Option<SourceChange> {
|
||||||
let parse = db.parse(position.file_id);
|
let parse = db.parse(position.file_id);
|
||||||
let syntax = parse.tree().syntax();
|
if let Some((ast_name, ast_module)) =
|
||||||
|
find_name_and_module_at_offset(parse.tree().syntax(), position)
|
||||||
if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) {
|
{
|
||||||
rename_mod(db, ast_name, ast_module, position, new_name)
|
rename_mod(db, &ast_name, &ast_module, position, new_name)
|
||||||
} else {
|
} else {
|
||||||
rename_reference(db, position, new_name)
|
rename_reference(db, position, new_name)
|
||||||
}
|
}
|
||||||
@ -101,14 +101,10 @@ pub(crate) fn rename(
|
|||||||
fn find_name_and_module_at_offset(
|
fn find_name_and_module_at_offset(
|
||||||
syntax: &SyntaxNode,
|
syntax: &SyntaxNode,
|
||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
) -> Option<(&ast::Name, &ast::Module)> {
|
) -> Option<(ast::Name, ast::Module)> {
|
||||||
let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset);
|
let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset)?;
|
||||||
let ast_name_parent = ast::Module::cast(ast_name?.syntax().parent()?);
|
let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?;
|
||||||
|
Some((ast_name, ast_module))
|
||||||
if let (Some(ast_module), Some(name)) = (ast_name_parent, ast_name) {
|
|
||||||
return Some((name, ast_module));
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn source_edit_from_fileid_range(
|
fn source_edit_from_fileid_range(
|
||||||
@ -135,7 +131,8 @@ fn rename_mod(
|
|||||||
) -> Option<SourceChange> {
|
) -> Option<SourceChange> {
|
||||||
let mut source_file_edits = Vec::new();
|
let mut source_file_edits = Vec::new();
|
||||||
let mut file_system_edits = Vec::new();
|
let mut file_system_edits = Vec::new();
|
||||||
if let Some(module) = source_binder::module_from_declaration(db, position.file_id, &ast_module)
|
if let Some(module) =
|
||||||
|
source_binder::module_from_declaration(db, position.file_id, ast_module.clone())
|
||||||
{
|
{
|
||||||
let src = module.definition_source(db);
|
let src = module.definition_source(db);
|
||||||
let file_id = src.file_id.as_original_file();
|
let file_id = src.file_id.as_original_file();
|
||||||
|
@ -26,8 +26,8 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
|
|||||||
parse.tree().syntax().descendants().filter_map(|i| runnable(db, file_id, i)).collect()
|
parse.tree().syntax().descendants().filter_map(|i| runnable(db, file_id, i)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runnable(db: &RootDatabase, file_id: FileId, item: &SyntaxNode) -> Option<Runnable> {
|
fn runnable(db: &RootDatabase, file_id: FileId, item: SyntaxNode) -> Option<Runnable> {
|
||||||
if let Some(fn_def) = ast::FnDef::cast(item) {
|
if let Some(fn_def) = ast::FnDef::cast(item.clone()) {
|
||||||
runnable_fn(fn_def)
|
runnable_fn(fn_def)
|
||||||
} else if let Some(m) = ast::Module::cast(item) {
|
} else if let Some(m) = ast::Module::cast(item) {
|
||||||
runnable_mod(db, file_id, m)
|
runnable_mod(db, file_id, m)
|
||||||
@ -36,8 +36,8 @@ fn runnable(db: &RootDatabase, file_id: FileId, item: &SyntaxNode) -> Option<Run
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runnable_fn(fn_def: &ast::FnDef) -> Option<Runnable> {
|
fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> {
|
||||||
let name = fn_def.name()?.text();
|
let name = fn_def.name()?.text().clone();
|
||||||
let kind = if name == "main" {
|
let kind = if name == "main" {
|
||||||
RunnableKind::Bin
|
RunnableKind::Bin
|
||||||
} else if fn_def.has_atom_attr("test") {
|
} else if fn_def.has_atom_attr("test") {
|
||||||
@ -50,7 +50,7 @@ fn runnable_fn(fn_def: &ast::FnDef) -> Option<Runnable> {
|
|||||||
Some(Runnable { range: fn_def.syntax().range(), kind })
|
Some(Runnable { range: fn_def.syntax().range(), kind })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runnable_mod(db: &RootDatabase, file_id: FileId, module: &ast::Module) -> Option<Runnable> {
|
fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> {
|
||||||
let has_test_function = module
|
let has_test_function = module
|
||||||
.item_list()?
|
.item_list()?
|
||||||
.items()
|
.items()
|
||||||
|
@ -104,7 +104,7 @@ impl FromIterator<TableEntry<MacroFile, Option<Parse<SyntaxNode>>>> for SyntaxTr
|
|||||||
let mut res = SyntaxTreeStats::default();
|
let mut res = SyntaxTreeStats::default();
|
||||||
for entry in iter {
|
for entry in iter {
|
||||||
res.total += 1;
|
res.total += 1;
|
||||||
if let Some(tree) = entry.value.and_then(|it| it).map(|it| it.tree().to_owned()) {
|
if let Some(tree) = entry.value.and_then(|it| it).map(|it| it.syntax_node()) {
|
||||||
res.retained += 1;
|
res.retained += 1;
|
||||||
res.retained_size += tree.memory_size_of_subtree();
|
res.retained_size += tree.memory_size_of_subtree();
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex>
|
|||||||
db.check_canceled();
|
db.check_canceled();
|
||||||
let parse = db.parse(file_id);
|
let parse = db.parse(file_id);
|
||||||
|
|
||||||
let symbols = source_file_to_file_symbols(parse.tree(), file_id);
|
let symbols = source_file_to_file_symbols(&parse.tree(), file_id);
|
||||||
|
|
||||||
// FIXME: add macros here
|
// FIXME: add macros here
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ impl SymbolIndex {
|
|||||||
files: impl ParallelIterator<Item = (FileId, Parse<ast::SourceFile>)>,
|
files: impl ParallelIterator<Item = (FileId, Parse<ast::SourceFile>)>,
|
||||||
) -> SymbolIndex {
|
) -> SymbolIndex {
|
||||||
let symbols = files
|
let symbols = files
|
||||||
.flat_map(|(file_id, file)| source_file_to_file_symbols(file.tree(), file_id))
|
.flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
SymbolIndex::new(symbols)
|
SymbolIndex::new(symbols)
|
||||||
}
|
}
|
||||||
@ -249,7 +249,7 @@ fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec
|
|||||||
for event in source_file.syntax().preorder() {
|
for event in source_file.syntax().preorder() {
|
||||||
match event {
|
match event {
|
||||||
WalkEvent::Enter(node) => {
|
WalkEvent::Enter(node) => {
|
||||||
if let Some(mut symbol) = to_file_symbol(node, file_id) {
|
if let Some(mut symbol) = to_file_symbol(&node, file_id) {
|
||||||
symbol.container_name = stack.last().cloned();
|
symbol.container_name = stack.last().cloned();
|
||||||
|
|
||||||
stack.push(symbol.name.clone());
|
stack.push(symbol.name.clone());
|
||||||
@ -258,7 +258,7 @@ fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec
|
|||||||
}
|
}
|
||||||
|
|
||||||
WalkEvent::Leave(node) => {
|
WalkEvent::Leave(node) => {
|
||||||
if to_symbol(node).is_some() {
|
if to_symbol(&node).is_some() {
|
||||||
stack.pop();
|
stack.pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -269,7 +269,7 @@ fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
|
fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
|
||||||
fn decl<N: NameOwner>(node: &N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
|
fn decl<N: NameOwner>(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> {
|
||||||
let name = node.name()?;
|
let name = node.name()?;
|
||||||
let name_range = name.syntax().range();
|
let name_range = name.syntax().range();
|
||||||
let name = name.text().clone();
|
let name = name.text().clone();
|
||||||
|
@ -31,8 +31,8 @@ fn is_control_keyword(kind: SyntaxKind) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_variable_mutable(db: &RootDatabase, analyzer: &hir::SourceAnalyzer, pat: &ast::Pat) -> bool {
|
fn is_variable_mutable(db: &RootDatabase, analyzer: &hir::SourceAnalyzer, pat: ast::Pat) -> bool {
|
||||||
let ty = analyzer.type_of_pat(db, pat).unwrap_or(Ty::Unknown);
|
let ty = analyzer.type_of_pat(db, &pat).unwrap_or(Ty::Unknown);
|
||||||
let is_ty_mut = {
|
let is_ty_mut = {
|
||||||
if let Some((_, mutability)) = ty.as_reference() {
|
if let Some((_, mutability)) = ty.as_reference() {
|
||||||
match mutability {
|
match mutability {
|
||||||
@ -55,7 +55,7 @@ fn is_variable_mutable(db: &RootDatabase, analyzer: &hir::SourceAnalyzer, pat: &
|
|||||||
pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> {
|
pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRange> {
|
||||||
let _p = profile("highlight");
|
let _p = profile("highlight");
|
||||||
let parse = db.parse(file_id);
|
let parse = db.parse(file_id);
|
||||||
let root = parse.tree().syntax();
|
let root = parse.tree().syntax().clone();
|
||||||
|
|
||||||
fn calc_binding_hash(file_id: FileId, text: &SmolStr, shadow_count: u32) -> u64 {
|
fn calc_binding_hash(file_id: FileId, text: &SmolStr, shadow_count: u32) -> u64 {
|
||||||
fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
|
fn hash<T: std::hash::Hash + std::fmt::Debug>(x: T) -> u64 {
|
||||||
@ -70,6 +70,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Visited nodes to handle highlighting priorities
|
// Visited nodes to handle highlighting priorities
|
||||||
|
// FIXME: retain only ranges here
|
||||||
let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default();
|
let mut highlighted: FxHashSet<SyntaxElement> = FxHashSet::default();
|
||||||
let mut bindings_shadow_count: FxHashMap<SmolStr, u32> = FxHashMap::default();
|
let mut bindings_shadow_count: FxHashMap<SmolStr, u32> = FxHashMap::default();
|
||||||
|
|
||||||
@ -84,14 +85,14 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
|
|||||||
STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string",
|
STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string",
|
||||||
ATTR => "attribute",
|
ATTR => "attribute",
|
||||||
NAME_REF => {
|
NAME_REF => {
|
||||||
if let Some(name_ref) = node.as_node().and_then(ast::NameRef::cast) {
|
if let Some(name_ref) = node.as_node().cloned().and_then(ast::NameRef::cast) {
|
||||||
// FIXME: revisit this after #1340
|
// FIXME: revisit this after #1340
|
||||||
use crate::name_ref_kind::{classify_name_ref, NameRefKind::*};
|
use crate::name_ref_kind::{classify_name_ref, NameRefKind::*};
|
||||||
use hir::{ImplItem, ModuleDef};
|
use hir::{ImplItem, ModuleDef};
|
||||||
|
|
||||||
// FIXME: try to reuse the SourceAnalyzers
|
// FIXME: try to reuse the SourceAnalyzers
|
||||||
let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
|
let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
|
||||||
match classify_name_ref(db, &analyzer, name_ref) {
|
match classify_name_ref(db, &analyzer, &name_ref) {
|
||||||
Some(Method(_)) => "function",
|
Some(Method(_)) => "function",
|
||||||
Some(Macro(_)) => "macro",
|
Some(Macro(_)) => "macro",
|
||||||
Some(FieldAccess(_)) => "field",
|
Some(FieldAccess(_)) => "field",
|
||||||
@ -113,13 +114,13 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
|
|||||||
Some(Pat(ptr)) => {
|
Some(Pat(ptr)) => {
|
||||||
binding_hash = Some({
|
binding_hash = Some({
|
||||||
let text =
|
let text =
|
||||||
ptr.syntax_node_ptr().to_node(root).text().to_smol_string();
|
ptr.syntax_node_ptr().to_node(&root).text().to_smol_string();
|
||||||
let shadow_count =
|
let shadow_count =
|
||||||
bindings_shadow_count.entry(text.clone()).or_default();
|
bindings_shadow_count.entry(text.clone()).or_default();
|
||||||
calc_binding_hash(file_id, &text, *shadow_count)
|
calc_binding_hash(file_id, &text, *shadow_count)
|
||||||
});
|
});
|
||||||
|
|
||||||
if is_variable_mutable(db, &analyzer, ptr.to_node(root)) {
|
if is_variable_mutable(db, &analyzer, ptr.to_node(&root)) {
|
||||||
"variable.mut"
|
"variable.mut"
|
||||||
} else {
|
} else {
|
||||||
"variable"
|
"variable"
|
||||||
@ -134,7 +135,7 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
NAME => {
|
NAME => {
|
||||||
if let Some(name) = node.as_node().and_then(ast::Name::cast) {
|
if let Some(name) = node.as_node().cloned().and_then(ast::Name::cast) {
|
||||||
let analyzer = hir::SourceAnalyzer::new(db, file_id, name.syntax(), None);
|
let analyzer = hir::SourceAnalyzer::new(db, file_id, name.syntax(), None);
|
||||||
if let Some(pat) = name.syntax().ancestors().find_map(ast::Pat::cast) {
|
if let Some(pat) = name.syntax().ancestors().find_map(ast::Pat::cast) {
|
||||||
binding_hash = Some({
|
binding_hash = Some({
|
||||||
@ -176,12 +177,11 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Vec<HighlightedRa
|
|||||||
k if is_control_keyword(k) => "keyword.control",
|
k if is_control_keyword(k) => "keyword.control",
|
||||||
k if k.is_keyword() => "keyword",
|
k if k.is_keyword() => "keyword",
|
||||||
_ => {
|
_ => {
|
||||||
// let analyzer = hir::SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
|
if let Some(macro_call) = node.as_node().cloned().and_then(ast::MacroCall::cast) {
|
||||||
if let Some(macro_call) = node.as_node().and_then(ast::MacroCall::cast) {
|
|
||||||
if let Some(path) = macro_call.path() {
|
if let Some(path) = macro_call.path() {
|
||||||
if let Some(segment) = path.segment() {
|
if let Some(segment) = path.segment() {
|
||||||
if let Some(name_ref) = segment.name_ref() {
|
if let Some(name_ref) = segment.name_ref() {
|
||||||
highlighted.insert(name_ref.syntax().into());
|
highlighted.insert(name_ref.syntax().clone().into());
|
||||||
let range_start = name_ref.syntax().range().start();
|
let range_start = name_ref.syntax().range().start();
|
||||||
let mut range_end = name_ref.syntax().range().end();
|
let mut range_end = name_ref.syntax().range().end();
|
||||||
for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
|
for sibling in path.syntax().siblings_with_tokens(Direction::Next) {
|
||||||
@ -230,7 +230,8 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
|
|||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
buf.push_str(&STYLE);
|
buf.push_str(&STYLE);
|
||||||
buf.push_str("<pre><code>");
|
buf.push_str("<pre><code>");
|
||||||
let tokens = parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.as_token());
|
let tokens =
|
||||||
|
parse.tree().syntax().descendants_with_tokens().filter_map(|it| it.as_token().cloned());
|
||||||
for token in tokens {
|
for token in tokens {
|
||||||
could_intersect.retain(|it| token.range().start() <= it.range.end());
|
could_intersect.retain(|it| token.range().start() <= it.range.end());
|
||||||
while let Some(r) = ranges.get(frontier) {
|
while let Some(r) = ranges.get(frontier) {
|
||||||
|
@ -18,7 +18,7 @@ pub(crate) fn syntax_tree(
|
|||||||
let node = match algo::find_covering_element(parse.tree().syntax(), text_range) {
|
let node = match algo::find_covering_element(parse.tree().syntax(), text_range) {
|
||||||
SyntaxElement::Node(node) => node,
|
SyntaxElement::Node(node) => node,
|
||||||
SyntaxElement::Token(token) => {
|
SyntaxElement::Token(token) => {
|
||||||
if let Some(tree) = syntax_tree_for_string(token, text_range) {
|
if let Some(tree) = syntax_tree_for_string(&token, text_range) {
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
token.parent()
|
token.parent()
|
||||||
@ -33,7 +33,7 @@ pub(crate) fn syntax_tree(
|
|||||||
|
|
||||||
/// Attempts parsing the selected contents of a string literal
|
/// Attempts parsing the selected contents of a string literal
|
||||||
/// as rust syntax and returns its syntax tree
|
/// as rust syntax and returns its syntax tree
|
||||||
fn syntax_tree_for_string(token: SyntaxToken, text_range: TextRange) -> Option<String> {
|
fn syntax_tree_for_string(token: &SyntaxToken, text_range: TextRange) -> Option<String> {
|
||||||
// When the range is inside a string
|
// When the range is inside a string
|
||||||
// we'll attempt parsing it as rust syntax
|
// we'll attempt parsing it as rust syntax
|
||||||
// to provide the syntax tree of the contents of the string
|
// to provide the syntax tree of the contents of the string
|
||||||
@ -43,7 +43,7 @@ fn syntax_tree_for_string(token: SyntaxToken, text_range: TextRange) -> Option<S
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn syntax_tree_for_token(node: SyntaxToken, text_range: TextRange) -> Option<String> {
|
fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<String> {
|
||||||
// Range of the full node
|
// Range of the full node
|
||||||
let node_range = node.range();
|
let node_range = node.range();
|
||||||
let text = node.text().to_string();
|
let text = node.text().to_string();
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
use crate::{db::RootDatabase, SourceChange, SourceFileEdit};
|
|
||||||
use ra_db::{FilePosition, SourceDatabase};
|
use ra_db::{FilePosition, SourceDatabase};
|
||||||
use ra_fmt::leading_indent;
|
use ra_fmt::leading_indent;
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo::{find_node_at_offset, find_token_at_offset, TokenAtOffset},
|
algo::{find_node_at_offset, find_token_at_offset, TokenAtOffset},
|
||||||
ast::{self, AstToken},
|
ast::{self, AstToken},
|
||||||
AstNode, SourceFile,
|
AstNode, SmolStr, SourceFile,
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
SyntaxToken, TextRange, TextUnit,
|
SyntaxToken, TextRange, TextUnit,
|
||||||
};
|
};
|
||||||
use ra_text_edit::{TextEdit, TextEditBuilder};
|
use ra_text_edit::{TextEdit, TextEditBuilder};
|
||||||
|
|
||||||
|
use crate::{db::RootDatabase, SourceChange, SourceFileEdit};
|
||||||
|
|
||||||
pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
|
pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
|
||||||
let parse = db.parse(position.file_id);
|
let parse = db.parse(position.file_id);
|
||||||
let file = parse.tree();
|
let file = parse.tree();
|
||||||
@ -43,15 +44,15 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Sour
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn node_indent<'a>(file: &'a SourceFile, token: SyntaxToken) -> Option<&'a str> {
|
fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
|
||||||
let ws = match find_token_at_offset(file.syntax(), token.range().start()) {
|
let ws = match find_token_at_offset(file.syntax(), token.range().start()) {
|
||||||
TokenAtOffset::Between(l, r) => {
|
TokenAtOffset::Between(l, r) => {
|
||||||
assert!(r == token);
|
assert!(r == *token);
|
||||||
l
|
l
|
||||||
}
|
}
|
||||||
TokenAtOffset::Single(n) => {
|
TokenAtOffset::Single(n) => {
|
||||||
assert!(n == token);
|
assert!(n == *token);
|
||||||
return Some("");
|
return Some("".into());
|
||||||
}
|
}
|
||||||
TokenAtOffset::None => unreachable!(),
|
TokenAtOffset::None => unreachable!(),
|
||||||
};
|
};
|
||||||
@ -60,12 +61,12 @@ fn node_indent<'a>(file: &'a SourceFile, token: SyntaxToken) -> Option<&'a str>
|
|||||||
}
|
}
|
||||||
let text = ws.text();
|
let text = ws.text();
|
||||||
let pos = text.rfind('\n').map(|it| it + 1).unwrap_or(0);
|
let pos = text.rfind('\n').map(|it| it + 1).unwrap_or(0);
|
||||||
Some(&text[pos..])
|
Some(text[pos..].into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_eq_typed(file: &SourceFile, eq_offset: TextUnit) -> Option<TextEdit> {
|
pub fn on_eq_typed(file: &SourceFile, eq_offset: TextUnit) -> Option<TextEdit> {
|
||||||
assert_eq!(file.syntax().text().char_at(eq_offset), Some('='));
|
assert_eq!(file.syntax().text().char_at(eq_offset), Some('='));
|
||||||
let let_stmt: &ast::LetStmt = find_node_at_offset(file.syntax(), eq_offset)?;
|
let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), eq_offset)?;
|
||||||
if let_stmt.has_semi() {
|
if let_stmt.has_semi() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -141,7 +142,7 @@ mod tests {
|
|||||||
edit.insert(offset, "=".to_string());
|
edit.insert(offset, "=".to_string());
|
||||||
let before = edit.finish().apply(&before);
|
let before = edit.finish().apply(&before);
|
||||||
let parse = SourceFile::parse(&before);
|
let parse = SourceFile::parse(&before);
|
||||||
if let Some(result) = on_eq_typed(parse.tree(), offset) {
|
if let Some(result) = on_eq_typed(&parse.tree(), offset) {
|
||||||
let actual = result.apply(&before);
|
let actual = result.apply(&before);
|
||||||
assert_eq_text!(after, &actual);
|
assert_eq_text!(after, &actual);
|
||||||
} else {
|
} else {
|
||||||
|
@ -599,7 +599,8 @@ mod tests {
|
|||||||
let macro_definition =
|
let macro_definition =
|
||||||
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
||||||
|
|
||||||
let (definition_tt, _) = ast_to_token_tree(macro_definition.token_tree().unwrap()).unwrap();
|
let (definition_tt, _) =
|
||||||
|
ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap();
|
||||||
crate::MacroRules::parse(&definition_tt).unwrap()
|
crate::MacroRules::parse(&definition_tt).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,7 +612,8 @@ mod tests {
|
|||||||
let macro_invocation =
|
let macro_invocation =
|
||||||
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
||||||
|
|
||||||
let (invocation_tt, _) = ast_to_token_tree(macro_invocation.token_tree().unwrap()).unwrap();
|
let (invocation_tt, _) =
|
||||||
|
ast_to_token_tree(¯o_invocation.token_tree().unwrap()).unwrap();
|
||||||
|
|
||||||
expand_rule(&rules.rules[0], &invocation_tt)
|
expand_rule(&rules.rules[0], &invocation_tt)
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,8 @@ mod tests {
|
|||||||
let macro_definition =
|
let macro_definition =
|
||||||
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
||||||
|
|
||||||
let (definition_tt, _) = ast_to_token_tree(macro_definition.token_tree().unwrap()).unwrap();
|
let (definition_tt, _) =
|
||||||
|
ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap();
|
||||||
parse(&definition_tt)
|
parse(&definition_tt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ use crate::subtree_source::SubtreeTokenSource;
|
|||||||
use crate::ExpandError;
|
use crate::ExpandError;
|
||||||
use ra_parser::{ParseError, TreeSink};
|
use ra_parser::{ParseError, TreeSink};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
ast, AstNode, Parse, SmolStr, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxNode,
|
ast, AstNode, AstToken, Parse, SmolStr, SyntaxElement, SyntaxKind, SyntaxKind::*, SyntaxNode,
|
||||||
SyntaxTreeBuilder, TextRange, TextUnit, T,
|
SyntaxTreeBuilder, TextRange, TextUnit, T,
|
||||||
};
|
};
|
||||||
use tt::buffer::{Cursor, TokenBuffer};
|
use tt::buffer::{Cursor, TokenBuffer};
|
||||||
@ -116,8 +116,6 @@ impl TokenMap {
|
|||||||
/// and strips the ending `*/`
|
/// and strips the ending `*/`
|
||||||
/// And then quote the string, which is needed to convert to `tt::Literal`
|
/// And then quote the string, which is needed to convert to `tt::Literal`
|
||||||
fn doc_comment_text(comment: &ast::Comment) -> SmolStr {
|
fn doc_comment_text(comment: &ast::Comment) -> SmolStr {
|
||||||
use ast::AstToken;
|
|
||||||
|
|
||||||
let prefix_len = comment.prefix().len();
|
let prefix_len = comment.prefix().len();
|
||||||
let mut text = &comment.text()[prefix_len..];
|
let mut text = &comment.text()[prefix_len..];
|
||||||
|
|
||||||
@ -132,9 +130,8 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr {
|
|||||||
text.into()
|
text.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_doc_comment<'a>(token: &ra_syntax::SyntaxToken<'a>) -> Option<Vec<tt::TokenTree>> {
|
fn convert_doc_comment(token: &ra_syntax::SyntaxToken) -> Option<Vec<tt::TokenTree>> {
|
||||||
use ast::AstToken;
|
let comment = ast::Comment::cast(token.clone())?;
|
||||||
let comment = ast::Comment::cast(*token)?;
|
|
||||||
let doc = comment.kind().doc?;
|
let doc = comment.kind().doc?;
|
||||||
|
|
||||||
// Make `doc="\" Comments\""
|
// Make `doc="\" Comments\""
|
||||||
@ -245,7 +242,7 @@ fn convert_tt(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SyntaxElement::Node(node) => {
|
SyntaxElement::Node(node) => {
|
||||||
let child = convert_tt(token_map, global_offset, node)?.into();
|
let child = convert_tt(token_map, global_offset, &node)?.into();
|
||||||
token_trees.push(child);
|
token_trees.push(child);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -37,8 +37,8 @@ impl_froms!(TokenTree: Leaf, Subtree);
|
|||||||
let macro_invocation =
|
let macro_invocation =
|
||||||
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
||||||
|
|
||||||
let (definition_tt, _) = ast_to_token_tree(macro_definition.token_tree().unwrap()).unwrap();
|
let (definition_tt, _) = ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap();
|
||||||
let (invocation_tt, _) = ast_to_token_tree(macro_invocation.token_tree().unwrap()).unwrap();
|
let (invocation_tt, _) = ast_to_token_tree(¯o_invocation.token_tree().unwrap()).unwrap();
|
||||||
let rules = crate::MacroRules::parse(&definition_tt).unwrap();
|
let rules = crate::MacroRules::parse(&definition_tt).unwrap();
|
||||||
let expansion = rules.expand(&invocation_tt).unwrap();
|
let expansion = rules.expand(&invocation_tt).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -53,7 +53,7 @@ pub(crate) fn create_rules(macro_definition: &str) -> MacroRules {
|
|||||||
let macro_definition =
|
let macro_definition =
|
||||||
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
||||||
|
|
||||||
let (definition_tt, _) = ast_to_token_tree(macro_definition.token_tree().unwrap()).unwrap();
|
let (definition_tt, _) = ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap();
|
||||||
crate::MacroRules::parse(&definition_tt).unwrap()
|
crate::MacroRules::parse(&definition_tt).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,34 +62,25 @@ pub(crate) fn expand(rules: &MacroRules, invocation: &str) -> tt::Subtree {
|
|||||||
let macro_invocation =
|
let macro_invocation =
|
||||||
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
|
||||||
|
|
||||||
let (invocation_tt, _) = ast_to_token_tree(macro_invocation.token_tree().unwrap()).unwrap();
|
let (invocation_tt, _) = ast_to_token_tree(¯o_invocation.token_tree().unwrap()).unwrap();
|
||||||
|
|
||||||
rules.expand(&invocation_tt).unwrap()
|
rules.expand(&invocation_tt).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn expand_to_items(
|
pub(crate) fn expand_to_items(rules: &MacroRules, invocation: &str) -> ast::MacroItems {
|
||||||
rules: &MacroRules,
|
|
||||||
invocation: &str,
|
|
||||||
) -> ra_syntax::TreeArc<ast::MacroItems> {
|
|
||||||
let expanded = expand(rules, invocation);
|
let expanded = expand(rules, invocation);
|
||||||
token_tree_to_macro_items(&expanded).unwrap().tree().to_owned()
|
token_tree_to_macro_items(&expanded).unwrap().tree()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub(crate) fn expand_to_stmts(
|
pub(crate) fn expand_to_stmts(rules: &MacroRules, invocation: &str) -> ast::MacroStmts {
|
||||||
rules: &MacroRules,
|
|
||||||
invocation: &str,
|
|
||||||
) -> ra_syntax::TreeArc<ast::MacroStmts> {
|
|
||||||
let expanded = expand(rules, invocation);
|
let expanded = expand(rules, invocation);
|
||||||
token_tree_to_macro_stmts(&expanded).unwrap().tree().to_owned()
|
token_tree_to_macro_stmts(&expanded).unwrap().tree()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn expand_to_expr(
|
pub(crate) fn expand_to_expr(rules: &MacroRules, invocation: &str) -> ast::Expr {
|
||||||
rules: &MacroRules,
|
|
||||||
invocation: &str,
|
|
||||||
) -> ra_syntax::TreeArc<ast::Expr> {
|
|
||||||
let expanded = expand(rules, invocation);
|
let expanded = expand(rules, invocation);
|
||||||
token_tree_to_expr(&expanded).unwrap().tree().to_owned()
|
token_tree_to_expr(&expanded).unwrap().tree()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn text_to_tokentree(text: &str) -> tt::Subtree {
|
pub(crate) fn text_to_tokentree(text: &str) -> tt::Subtree {
|
||||||
@ -97,7 +88,7 @@ pub(crate) fn text_to_tokentree(text: &str) -> tt::Subtree {
|
|||||||
let wrapped = format!("wrap_macro!( {} )", text);
|
let wrapped = format!("wrap_macro!( {} )", text);
|
||||||
let wrapped = ast::SourceFile::parse(&wrapped);
|
let wrapped = ast::SourceFile::parse(&wrapped);
|
||||||
let wrapped = wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
let wrapped = wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||||
let mut wrapped = ast_to_token_tree(wrapped).unwrap().0;
|
let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0;
|
||||||
wrapped.delimiter = tt::Delimiter::None;
|
wrapped.delimiter = tt::Delimiter::None;
|
||||||
|
|
||||||
wrapped
|
wrapped
|
||||||
@ -164,8 +155,8 @@ pub(crate) fn assert_expansion(
|
|||||||
|
|
||||||
let (expanded_tree, expected_tree) = match kind {
|
let (expanded_tree, expected_tree) = match kind {
|
||||||
MacroKind::Items => {
|
MacroKind::Items => {
|
||||||
let expanded_tree = token_tree_to_macro_items(&expanded).unwrap().tree().to_owned();
|
let expanded_tree = token_tree_to_macro_items(&expanded).unwrap().tree();
|
||||||
let expected_tree = token_tree_to_macro_items(&expected).unwrap().tree().to_owned();
|
let expected_tree = token_tree_to_macro_items(&expected).unwrap().tree();
|
||||||
|
|
||||||
(
|
(
|
||||||
debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(),
|
debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(),
|
||||||
@ -174,8 +165,8 @@ pub(crate) fn assert_expansion(
|
|||||||
}
|
}
|
||||||
|
|
||||||
MacroKind::Stmts => {
|
MacroKind::Stmts => {
|
||||||
let expanded_tree = token_tree_to_macro_stmts(&expanded).unwrap().tree().to_owned();
|
let expanded_tree = token_tree_to_macro_stmts(&expanded).unwrap().tree();
|
||||||
let expected_tree = token_tree_to_macro_stmts(&expected).unwrap().tree().to_owned();
|
let expected_tree = token_tree_to_macro_stmts(&expected).unwrap().tree();
|
||||||
|
|
||||||
(
|
(
|
||||||
debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(),
|
debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(),
|
||||||
@ -419,7 +410,7 @@ fn test_expand_to_item_list() {
|
|||||||
",
|
",
|
||||||
);
|
);
|
||||||
let expansion = expand(&rules, "structs!(Foo, Bar);");
|
let expansion = expand(&rules, "structs!(Foo, Bar);");
|
||||||
let tree = token_tree_to_macro_items(&expansion).unwrap().tree().to_owned();
|
let tree = token_tree_to_macro_items(&expansion).unwrap().tree();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tree.syntax().debug_dump().trim(),
|
tree.syntax().debug_dump().trim(),
|
||||||
r#"
|
r#"
|
||||||
@ -537,7 +528,7 @@ fn test_tt_to_stmts() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let expanded = expand(&rules, "foo!{}");
|
let expanded = expand(&rules, "foo!{}");
|
||||||
let stmts = token_tree_to_macro_stmts(&expanded).unwrap().tree().to_owned();
|
let stmts = token_tree_to_macro_stmts(&expanded).unwrap().tree();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
stmts.syntax().debug_dump().trim(),
|
stmts.syntax().debug_dump().trim(),
|
||||||
|
@ -10,7 +10,7 @@ repository = "https://github.com/rust-analyzer/rust-analyzer"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
unicode-xid = "0.1.0"
|
unicode-xid = "0.1.0"
|
||||||
itertools = "0.8.0"
|
itertools = "0.8.0"
|
||||||
rowan = "0.5.0"
|
rowan = "0.5.6"
|
||||||
|
|
||||||
# ideally, `serde` should be enabled by `ra_lsp_server`, but we enable it here
|
# ideally, `serde` should be enabled by `ra_lsp_server`, but we enable it here
|
||||||
# to reduce number of compilations
|
# to reduce number of compilations
|
||||||
|
@ -9,8 +9,8 @@ pub use rowan::TokenAtOffset;
|
|||||||
pub fn find_token_at_offset(node: &SyntaxNode, offset: TextUnit) -> TokenAtOffset<SyntaxToken> {
|
pub fn find_token_at_offset(node: &SyntaxNode, offset: TextUnit) -> TokenAtOffset<SyntaxToken> {
|
||||||
match node.0.token_at_offset(offset) {
|
match node.0.token_at_offset(offset) {
|
||||||
TokenAtOffset::None => TokenAtOffset::None,
|
TokenAtOffset::None => TokenAtOffset::None,
|
||||||
TokenAtOffset::Single(n) => TokenAtOffset::Single(n.into()),
|
TokenAtOffset::Single(n) => TokenAtOffset::Single(SyntaxToken(n)),
|
||||||
TokenAtOffset::Between(l, r) => TokenAtOffset::Between(l.into(), r.into()),
|
TokenAtOffset::Between(l, r) => TokenAtOffset::Between(SyntaxToken(l), SyntaxToken(r)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ pub fn find_token_at_offset(node: &SyntaxNode, offset: TextUnit) -> TokenAtOffse
|
|||||||
pub fn ancestors_at_offset(
|
pub fn ancestors_at_offset(
|
||||||
node: &SyntaxNode,
|
node: &SyntaxNode,
|
||||||
offset: TextUnit,
|
offset: TextUnit,
|
||||||
) -> impl Iterator<Item = &SyntaxNode> {
|
) -> impl Iterator<Item = SyntaxNode> {
|
||||||
find_token_at_offset(node, offset)
|
find_token_at_offset(node, offset)
|
||||||
.map(|token| token.parent().ancestors())
|
.map(|token| token.parent().ancestors())
|
||||||
.kmerge_by(|node1, node2| node1.range().len() < node2.range().len())
|
.kmerge_by(|node1, node2| node1.range().len() < node2.range().len())
|
||||||
@ -37,7 +37,7 @@ pub fn ancestors_at_offset(
|
|||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// then the shorter node will be silently preferred.
|
/// then the shorter node will be silently preferred.
|
||||||
pub fn find_node_at_offset<N: AstNode>(syntax: &SyntaxNode, offset: TextUnit) -> Option<&N> {
|
pub fn find_node_at_offset<N: AstNode>(syntax: &SyntaxNode, offset: TextUnit) -> Option<N> {
|
||||||
ancestors_at_offset(syntax, offset).find_map(N::cast)
|
ancestors_at_offset(syntax, offset).find_map(N::cast)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,5 +59,5 @@ pub fn non_trivia_sibling(element: SyntaxElement, direction: Direction) -> Optio
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_covering_element(root: &SyntaxNode, range: TextRange) -> SyntaxElement {
|
pub fn find_covering_element(root: &SyntaxNode, range: TextRange) -> SyntaxElement {
|
||||||
root.0.covering_node(range).into()
|
SyntaxElement::new(root.0.covering_node(range))
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ pub trait Visitor<'a>: Sized {
|
|||||||
fn visit<N, F>(self, f: F) -> Vis<Self, N, F>
|
fn visit<N, F>(self, f: F) -> Vis<Self, N, F>
|
||||||
where
|
where
|
||||||
N: AstNode + 'a,
|
N: AstNode + 'a,
|
||||||
F: FnOnce(&'a N) -> Self::Output,
|
F: FnOnce(N) -> Self::Output,
|
||||||
{
|
{
|
||||||
Vis { inner: self, f, ph: PhantomData }
|
Vis { inner: self, f, ph: PhantomData }
|
||||||
}
|
}
|
||||||
@ -29,7 +29,7 @@ pub trait VisitorCtx<'a>: Sized {
|
|||||||
fn visit<N, F>(self, f: F) -> VisCtx<Self, N, F>
|
fn visit<N, F>(self, f: F) -> VisCtx<Self, N, F>
|
||||||
where
|
where
|
||||||
N: AstNode + 'a,
|
N: AstNode + 'a,
|
||||||
F: FnOnce(&'a N, Self::Ctx) -> Self::Output,
|
F: FnOnce(N, Self::Ctx) -> Self::Output,
|
||||||
{
|
{
|
||||||
VisCtx { inner: self, f, ph: PhantomData }
|
VisCtx { inner: self, f, ph: PhantomData }
|
||||||
}
|
}
|
||||||
@ -74,13 +74,13 @@ impl<'a, V, N, F> Visitor<'a> for Vis<V, N, F>
|
|||||||
where
|
where
|
||||||
V: Visitor<'a>,
|
V: Visitor<'a>,
|
||||||
N: AstNode + 'a,
|
N: AstNode + 'a,
|
||||||
F: FnOnce(&'a N) -> <V as Visitor<'a>>::Output,
|
F: FnOnce(N) -> <V as Visitor<'a>>::Output,
|
||||||
{
|
{
|
||||||
type Output = <V as Visitor<'a>>::Output;
|
type Output = <V as Visitor<'a>>::Output;
|
||||||
|
|
||||||
fn accept(self, node: &'a SyntaxNode) -> Option<Self::Output> {
|
fn accept(self, node: &'a SyntaxNode) -> Option<Self::Output> {
|
||||||
let Vis { inner, f, .. } = self;
|
let Vis { inner, f, .. } = self;
|
||||||
inner.accept(node).or_else(|| N::cast(node).map(f))
|
inner.accept(node).or_else(|| N::cast(node.clone()).map(f))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,14 +95,14 @@ impl<'a, V, N, F> VisitorCtx<'a> for VisCtx<V, N, F>
|
|||||||
where
|
where
|
||||||
V: VisitorCtx<'a>,
|
V: VisitorCtx<'a>,
|
||||||
N: AstNode + 'a,
|
N: AstNode + 'a,
|
||||||
F: FnOnce(&'a N, <V as VisitorCtx<'a>>::Ctx) -> <V as VisitorCtx<'a>>::Output,
|
F: FnOnce(N, <V as VisitorCtx<'a>>::Ctx) -> <V as VisitorCtx<'a>>::Output,
|
||||||
{
|
{
|
||||||
type Output = <V as VisitorCtx<'a>>::Output;
|
type Output = <V as VisitorCtx<'a>>::Output;
|
||||||
type Ctx = <V as VisitorCtx<'a>>::Ctx;
|
type Ctx = <V as VisitorCtx<'a>>::Ctx;
|
||||||
|
|
||||||
fn accept(self, node: &'a SyntaxNode) -> Result<Self::Output, Self::Ctx> {
|
fn accept(self, node: &'a SyntaxNode) -> Result<Self::Output, Self::Ctx> {
|
||||||
let VisCtx { inner, f, .. } = self;
|
let VisCtx { inner, f, .. } = self;
|
||||||
inner.accept(node).or_else(|ctx| match N::cast(node) {
|
inner.accept(node).or_else(|ctx| match N::cast(node.clone()) {
|
||||||
None => Err(ctx),
|
None => Err(ctx),
|
||||||
Some(node) => Ok(f(node, ctx)),
|
Some(node) => Ok(f(node, ctx)),
|
||||||
})
|
})
|
||||||
|
@ -9,7 +9,7 @@ mod expr_extensions;
|
|||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken, TreeArc},
|
syntax_node::{SyntaxNode, SyntaxNodeChildren, SyntaxToken},
|
||||||
SmolStr,
|
SmolStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -25,51 +25,49 @@ pub use self::{
|
|||||||
/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
|
/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
|
||||||
/// the same representation: a pointer to the tree root and a pointer to the
|
/// the same representation: a pointer to the tree root and a pointer to the
|
||||||
/// node itself.
|
/// node itself.
|
||||||
pub trait AstNode:
|
pub trait AstNode: Clone {
|
||||||
rowan::TransparentNewType<Repr = rowan::SyntaxNode> + ToOwned<Owned = TreeArc<Self>>
|
fn cast(syntax: SyntaxNode) -> Option<Self>
|
||||||
{
|
|
||||||
fn cast(syntax: &SyntaxNode) -> Option<&Self>
|
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
fn syntax(&self) -> &SyntaxNode;
|
fn syntax(&self) -> &SyntaxNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like `AstNode`, but wraps tokens rather than interior nodes.
|
/// Like `AstNode`, but wraps tokens rather than interior nodes.
|
||||||
pub trait AstToken<'a> {
|
pub trait AstToken {
|
||||||
fn cast(token: SyntaxToken<'a>) -> Option<Self>
|
fn cast(token: SyntaxToken) -> Option<Self>
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
fn syntax(&self) -> SyntaxToken<'a>;
|
fn syntax(&self) -> &SyntaxToken;
|
||||||
fn text(&self) -> &'a SmolStr {
|
fn text(&self) -> &SmolStr {
|
||||||
self.syntax().text()
|
self.syntax().text()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An iterator over `SyntaxNode` children of a particular AST type.
|
/// An iterator over `SyntaxNode` children of a particular AST type.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AstChildren<'a, N> {
|
pub struct AstChildren<N> {
|
||||||
inner: SyntaxNodeChildren<'a>,
|
inner: SyntaxNodeChildren,
|
||||||
ph: PhantomData<N>,
|
ph: PhantomData<N>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, N> AstChildren<'a, N> {
|
impl<N> AstChildren<N> {
|
||||||
fn new(parent: &'a SyntaxNode) -> Self {
|
fn new(parent: &SyntaxNode) -> Self {
|
||||||
AstChildren { inner: parent.children(), ph: PhantomData }
|
AstChildren { inner: parent.children(), ph: PhantomData }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, N: AstNode + 'a> Iterator for AstChildren<'a, N> {
|
impl<N: AstNode> Iterator for AstChildren<N> {
|
||||||
type Item = &'a N;
|
type Item = N;
|
||||||
fn next(&mut self) -> Option<&'a N> {
|
fn next(&mut self) -> Option<N> {
|
||||||
self.inner.by_ref().find_map(N::cast)
|
self.inner.by_ref().find_map(N::cast)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn child_opt<P: AstNode, C: AstNode>(parent: &P) -> Option<&C> {
|
fn child_opt<P: AstNode + ?Sized, C: AstNode>(parent: &P) -> Option<C> {
|
||||||
children(parent).next()
|
children(parent).next()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children<P: AstNode, C: AstNode>(parent: &P) -> AstChildren<C> {
|
fn children<P: AstNode + ?Sized, C: AstNode>(parent: &P) -> AstChildren<C> {
|
||||||
AstChildren::new(parent.syntax())
|
AstChildren::new(parent.syntax())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +121,7 @@ fn test_doc_comment_preserves_indents() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_where_predicates() {
|
fn test_where_predicates() {
|
||||||
fn assert_bound(text: &str, bound: Option<&TypeBound>) {
|
fn assert_bound(text: &str, bound: Option<TypeBound>) {
|
||||||
assert_eq!(text, bound.unwrap().syntax().text().to_string());
|
assert_eq!(text, bound.unwrap().syntax().text().to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,20 +8,20 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum ElseBranch<'a> {
|
pub enum ElseBranch {
|
||||||
Block(&'a ast::Block),
|
Block(ast::Block),
|
||||||
IfExpr(&'a ast::IfExpr),
|
IfExpr(ast::IfExpr),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::IfExpr {
|
impl ast::IfExpr {
|
||||||
pub fn then_branch(&self) -> Option<&ast::Block> {
|
pub fn then_branch(&self) -> Option<ast::Block> {
|
||||||
self.blocks().nth(0)
|
self.blocks().nth(0)
|
||||||
}
|
}
|
||||||
pub fn else_branch(&self) -> Option<ElseBranch> {
|
pub fn else_branch(&self) -> Option<ElseBranch> {
|
||||||
let res = match self.blocks().nth(1) {
|
let res = match self.blocks().nth(1) {
|
||||||
Some(block) => ElseBranch::Block(block),
|
Some(block) => ElseBranch::Block(block),
|
||||||
None => {
|
None => {
|
||||||
let elif: &ast::IfExpr = child_opt(self)?;
|
let elif: ast::IfExpr = child_opt(self)?;
|
||||||
ElseBranch::IfExpr(elif)
|
ElseBranch::IfExpr(elif)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -60,7 +60,7 @@ impl ast::PrefixExpr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn op_token(&self) -> Option<SyntaxToken> {
|
pub fn op_token(&self) -> Option<SyntaxToken> {
|
||||||
self.syntax().first_child_or_token()?.as_token()
|
self.syntax().first_child_or_token()?.as_token().cloned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ pub enum BinOp {
|
|||||||
|
|
||||||
impl ast::BinExpr {
|
impl ast::BinExpr {
|
||||||
fn op_details(&self) -> Option<(SyntaxToken, BinOp)> {
|
fn op_details(&self) -> Option<(SyntaxToken, BinOp)> {
|
||||||
self.syntax().children_with_tokens().filter_map(|it| it.as_token()).find_map(|c| {
|
self.syntax().children_with_tokens().filter_map(|it| it.as_token().cloned()).find_map(|c| {
|
||||||
match c.kind() {
|
match c.kind() {
|
||||||
T![||] => Some((c, BinOp::BooleanOr)),
|
T![||] => Some((c, BinOp::BooleanOr)),
|
||||||
T![&&] => Some((c, BinOp::BooleanAnd)),
|
T![&&] => Some((c, BinOp::BooleanAnd)),
|
||||||
@ -178,15 +178,15 @@ impl ast::BinExpr {
|
|||||||
self.op_details().map(|t| t.0)
|
self.op_details().map(|t| t.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lhs(&self) -> Option<&ast::Expr> {
|
pub fn lhs(&self) -> Option<ast::Expr> {
|
||||||
children(self).nth(0)
|
children(self).nth(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rhs(&self) -> Option<&ast::Expr> {
|
pub fn rhs(&self) -> Option<ast::Expr> {
|
||||||
children(self).nth(1)
|
children(self).nth(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sub_exprs(&self) -> (Option<&ast::Expr>, Option<&ast::Expr>) {
|
pub fn sub_exprs(&self) -> (Option<ast::Expr>, Option<ast::Expr>) {
|
||||||
let mut children = children(self);
|
let mut children = children(self);
|
||||||
let first = children.next();
|
let first = children.next();
|
||||||
let second = children.next();
|
let second = children.next();
|
||||||
@ -194,9 +194,9 @@ impl ast::BinExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ArrayExprKind<'a> {
|
pub enum ArrayExprKind {
|
||||||
Repeat { initializer: Option<&'a ast::Expr>, repeat: Option<&'a ast::Expr> },
|
Repeat { initializer: Option<ast::Expr>, repeat: Option<ast::Expr> },
|
||||||
ElementList(AstChildren<'a, ast::Expr>),
|
ElementList(AstChildren<ast::Expr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::ArrayExpr {
|
impl ast::ArrayExpr {
|
||||||
@ -275,12 +275,12 @@ impl ast::Literal {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_literal_with_attr() {
|
fn test_literal_with_attr() {
|
||||||
let parse = ast::SourceFile::parse(r#"const _: &str = { #[attr] "Hello" };"#);
|
let parse = ast::SourceFile::parse(r#"const _: &str = { #[attr] "Hello" };"#);
|
||||||
let lit = parse.tree.syntax().descendants().find_map(ast::Literal::cast).unwrap();
|
let lit = parse.tree().syntax().descendants().find_map(ast::Literal::cast).unwrap();
|
||||||
assert_eq!(lit.token().text(), r#""Hello""#);
|
assert_eq!(lit.token().text(), r#""Hello""#);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::NamedField {
|
impl ast::NamedField {
|
||||||
pub fn parent_struct_lit(&self) -> &ast::StructLit {
|
pub fn parent_struct_lit(&self) -> ast::StructLit {
|
||||||
self.syntax().ancestors().find_map(ast::StructLit::cast).unwrap()
|
self.syntax().ancestors().find_map(ast::StructLit::cast).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{self, child_opt, children, AstNode},
|
ast::{self, child_opt, children, AstNode, SyntaxNode},
|
||||||
SmolStr, SyntaxElement,
|
SmolStr, SyntaxElement,
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
SyntaxToken, T,
|
SyntaxToken, T,
|
||||||
@ -13,15 +13,20 @@ use ra_parser::SyntaxKind;
|
|||||||
|
|
||||||
impl ast::Name {
|
impl ast::Name {
|
||||||
pub fn text(&self) -> &SmolStr {
|
pub fn text(&self) -> &SmolStr {
|
||||||
let ident = self.syntax().first_child_or_token().unwrap().as_token().unwrap();
|
text_of_first_token(self.syntax())
|
||||||
ident.text()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::NameRef {
|
impl ast::NameRef {
|
||||||
pub fn text(&self) -> &SmolStr {
|
pub fn text(&self) -> &SmolStr {
|
||||||
let ident = self.syntax().first_child_or_token().unwrap().as_token().unwrap();
|
text_of_first_token(self.syntax())
|
||||||
ident.text()
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text_of_first_token(node: &SyntaxNode) -> &SmolStr {
|
||||||
|
match node.0.green().children().first() {
|
||||||
|
Some(rowan::GreenElement::Token(it)) => it.text(),
|
||||||
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,10 +55,10 @@ impl ast::Attr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_call(&self) -> Option<(SmolStr, &ast::TokenTree)> {
|
pub fn as_call(&self) -> Option<(SmolStr, ast::TokenTree)> {
|
||||||
let tt = self.value()?;
|
let tt = self.value()?;
|
||||||
let (_bra, attr, args, _ket) = tt.syntax().children_with_tokens().collect_tuple()?;
|
let (_bra, attr, args, _ket) = tt.syntax().children_with_tokens().collect_tuple()?;
|
||||||
let args = ast::TokenTree::cast(args.as_node()?)?;
|
let args = ast::TokenTree::cast(args.as_node()?.clone())?;
|
||||||
if attr.kind() == IDENT {
|
if attr.kind() == IDENT {
|
||||||
Some((attr.as_token()?.text().clone(), args))
|
Some((attr.as_token()?.text().clone(), args))
|
||||||
} else {
|
} else {
|
||||||
@ -86,16 +91,16 @@ impl ast::Attr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum PathSegmentKind<'a> {
|
pub enum PathSegmentKind {
|
||||||
Name(&'a ast::NameRef),
|
Name(ast::NameRef),
|
||||||
SelfKw,
|
SelfKw,
|
||||||
SuperKw,
|
SuperKw,
|
||||||
CrateKw,
|
CrateKw,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::PathSegment {
|
impl ast::PathSegment {
|
||||||
pub fn parent_path(&self) -> &ast::Path {
|
pub fn parent_path(&self) -> ast::Path {
|
||||||
self.syntax()
|
self.syntax()
|
||||||
.parent()
|
.parent()
|
||||||
.and_then(ast::Path::cast)
|
.and_then(ast::Path::cast)
|
||||||
@ -125,7 +130,7 @@ impl ast::PathSegment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ast::Path {
|
impl ast::Path {
|
||||||
pub fn parent_path(&self) -> Option<&ast::Path> {
|
pub fn parent_path(&self) -> Option<ast::Path> {
|
||||||
self.syntax().parent().and_then(ast::Path::cast)
|
self.syntax().parent().and_then(ast::Path::cast)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,7 +151,7 @@ impl ast::UseTree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ast::UseTreeList {
|
impl ast::UseTreeList {
|
||||||
pub fn parent_use_tree(&self) -> &ast::UseTree {
|
pub fn parent_use_tree(&self) -> ast::UseTree {
|
||||||
self.syntax()
|
self.syntax()
|
||||||
.parent()
|
.parent()
|
||||||
.and_then(ast::UseTree::cast)
|
.and_then(ast::UseTree::cast)
|
||||||
@ -155,21 +160,21 @@ impl ast::UseTreeList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ast::ImplBlock {
|
impl ast::ImplBlock {
|
||||||
pub fn target_type(&self) -> Option<&ast::TypeRef> {
|
pub fn target_type(&self) -> Option<ast::TypeRef> {
|
||||||
match self.target() {
|
match self.target() {
|
||||||
(Some(t), None) | (_, Some(t)) => Some(t),
|
(Some(t), None) | (_, Some(t)) => Some(t),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn target_trait(&self) -> Option<&ast::TypeRef> {
|
pub fn target_trait(&self) -> Option<ast::TypeRef> {
|
||||||
match self.target() {
|
match self.target() {
|
||||||
(Some(t), Some(_)) => Some(t),
|
(Some(t), Some(_)) => Some(t),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn target(&self) -> (Option<&ast::TypeRef>, Option<&ast::TypeRef>) {
|
fn target(&self) -> (Option<ast::TypeRef>, Option<ast::TypeRef>) {
|
||||||
let mut types = children(self);
|
let mut types = children(self);
|
||||||
let first = types.next();
|
let first = types.next();
|
||||||
let second = types.next();
|
let second = types.next();
|
||||||
@ -182,13 +187,13 @@ impl ast::ImplBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum StructKind<'a> {
|
pub enum StructKind {
|
||||||
Tuple(&'a ast::PosFieldDefList),
|
Tuple(ast::PosFieldDefList),
|
||||||
Named(&'a ast::NamedFieldDefList),
|
Named(ast::NamedFieldDefList),
|
||||||
Unit,
|
Unit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructKind<'_> {
|
impl StructKind {
|
||||||
fn from_node<N: AstNode>(node: &N) -> StructKind {
|
fn from_node<N: AstNode>(node: &N) -> StructKind {
|
||||||
if let Some(nfdl) = child_opt::<_, ast::NamedFieldDefList>(node) {
|
if let Some(nfdl) = child_opt::<_, ast::NamedFieldDefList>(node) {
|
||||||
StructKind::Named(nfdl)
|
StructKind::Named(nfdl)
|
||||||
@ -218,7 +223,7 @@ impl ast::StructDef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ast::EnumVariant {
|
impl ast::EnumVariant {
|
||||||
pub fn parent_enum(&self) -> &ast::EnumDef {
|
pub fn parent_enum(&self) -> ast::EnumDef {
|
||||||
self.syntax()
|
self.syntax()
|
||||||
.parent()
|
.parent()
|
||||||
.and_then(|it| it.parent())
|
.and_then(|it| it.parent())
|
||||||
@ -231,10 +236,10 @@ impl ast::EnumVariant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ast::FnDef {
|
impl ast::FnDef {
|
||||||
pub fn semicolon_token(&self) -> Option<SyntaxToken<'_>> {
|
pub fn semicolon_token(&self) -> Option<SyntaxToken> {
|
||||||
self.syntax()
|
self.syntax()
|
||||||
.last_child_or_token()
|
.last_child_or_token()
|
||||||
.and_then(|it| it.as_token())
|
.and_then(|it| it.as_token().cloned())
|
||||||
.filter(|it| it.kind() == T![;])
|
.filter(|it| it.kind() == T![;])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,9 +263,9 @@ impl ast::ExprStmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum FieldKind<'a> {
|
pub enum FieldKind {
|
||||||
Name(&'a ast::NameRef),
|
Name(ast::NameRef),
|
||||||
Index(SyntaxToken<'a>),
|
Index(SyntaxToken),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ast::FieldExpr {
|
impl ast::FieldExpr {
|
||||||
@ -271,6 +276,7 @@ impl ast::FieldExpr {
|
|||||||
.find(|c| c.kind() == SyntaxKind::INT_NUMBER || c.kind() == SyntaxKind::FLOAT_NUMBER)
|
.find(|c| c.kind() == SyntaxKind::INT_NUMBER || c.kind() == SyntaxKind::FLOAT_NUMBER)
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(SyntaxElement::as_token)
|
.and_then(SyntaxElement::as_token)
|
||||||
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn field_access(&self) -> Option<FieldKind> {
|
pub fn field_access(&self) -> Option<FieldKind> {
|
||||||
@ -326,7 +332,7 @@ impl ast::SelfParam {
|
|||||||
pub fn self_kw_token(&self) -> SyntaxToken {
|
pub fn self_kw_token(&self) -> SyntaxToken {
|
||||||
self.syntax()
|
self.syntax()
|
||||||
.children_with_tokens()
|
.children_with_tokens()
|
||||||
.filter_map(|it| it.as_token())
|
.filter_map(|it| it.as_token().cloned())
|
||||||
.find(|it| it.kind() == T![self])
|
.find(|it| it.kind() == T![self])
|
||||||
.expect("invalid tree: self param must have self")
|
.expect("invalid tree: self param must have self")
|
||||||
}
|
}
|
||||||
@ -355,7 +361,7 @@ impl ast::LifetimeParam {
|
|||||||
pub fn lifetime_token(&self) -> Option<SyntaxToken> {
|
pub fn lifetime_token(&self) -> Option<SyntaxToken> {
|
||||||
self.syntax()
|
self.syntax()
|
||||||
.children_with_tokens()
|
.children_with_tokens()
|
||||||
.filter_map(|it| it.as_token())
|
.filter_map(|it| it.as_token().cloned())
|
||||||
.find(|it| it.kind() == LIFETIME)
|
.find(|it| it.kind() == LIFETIME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -364,7 +370,7 @@ impl ast::WherePred {
|
|||||||
pub fn lifetime_token(&self) -> Option<SyntaxToken> {
|
pub fn lifetime_token(&self) -> Option<SyntaxToken> {
|
||||||
self.syntax()
|
self.syntax()
|
||||||
.children_with_tokens()
|
.children_with_tokens()
|
||||||
.filter_map(|it| it.as_token())
|
.filter_map(|it| it.as_token().cloned())
|
||||||
.find(|it| it.kind() == LIFETIME)
|
.find(|it| it.kind() == LIFETIME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -11,94 +11,73 @@ the below applies to the result of this template
|
|||||||
|
|
||||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
|
||||||
use rowan::TransparentNewType;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
SyntaxNode, SyntaxKind::*,
|
SyntaxNode, SyntaxKind::*,
|
||||||
syntax_node::{TreeArc},
|
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
};
|
};
|
||||||
{% for node, methods in ast %}
|
{% for node, methods in ast %}
|
||||||
// {{ node }}
|
// {{ node }}
|
||||||
|
|
||||||
{%- if methods.enum %}
|
{%- if methods.enum %}
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct {{ node }} {
|
pub struct {{ node }} {
|
||||||
pub(crate) syntax: SyntaxNode,
|
pub(crate) syntax: SyntaxNode,
|
||||||
}
|
}
|
||||||
unsafe impl TransparentNewType for {{ node }} {
|
|
||||||
type Repr = rowan::SyntaxNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum {{ node }}Kind<'a> {
|
pub enum {{ node }}Kind {
|
||||||
{%- for kind in methods.enum %}
|
{%- for kind in methods.enum %}
|
||||||
{{ kind }}(&'a {{ kind }}),
|
{{ kind }}({{ kind }}),
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
}
|
}
|
||||||
|
|
||||||
{%- for kind in methods.enum %}
|
{%- for kind in methods.enum %}
|
||||||
impl<'a> From<&'a {{ kind }}> for &'a {{ node }} {
|
impl From<{{ kind }}> for {{ node }} {
|
||||||
fn from(n: &'a {{ kind }}) -> &'a {{ node }} {
|
fn from(n: {{ kind }}) -> {{ node }} {
|
||||||
{{ node }}::cast(&n.syntax).unwrap()
|
{{ node }}::cast(n.syntax).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
|
|
||||||
|
|
||||||
impl AstNode for {{ node }} {
|
impl AstNode for {{ node }} {
|
||||||
fn cast(syntax: &SyntaxNode) -> Option<&Self> {
|
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||||
match syntax.kind() {
|
match syntax.kind() {
|
||||||
{%- for kind in methods.enum %}
|
{%- for kind in methods.enum %}
|
||||||
| {{ kind | SCREAM }}
|
| {{ kind | SCREAM }}
|
||||||
{%- endfor %} => Some({{ node }}::from_repr(syntax.into_repr())),
|
{%- endfor %} => Some({{ node }} { syntax }),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToOwned for {{ node }} {
|
|
||||||
type Owned = TreeArc<{{ node }}>;
|
|
||||||
fn to_owned(&self) -> TreeArc<{{ node }}> { TreeArc::cast(self.syntax.to_owned()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl {{ node }} {
|
impl {{ node }} {
|
||||||
pub fn kind(&self) -> {{ node }}Kind {
|
pub fn kind(&self) -> {{ node }}Kind {
|
||||||
match self.syntax.kind() {
|
match self.syntax.kind() {
|
||||||
{%- for kind in methods.enum %}
|
{%- for kind in methods.enum %}
|
||||||
{{ kind | SCREAM }} => {{ node }}Kind::{{ kind }}({{ kind }}::cast(&self.syntax).unwrap()),
|
{{ kind | SCREAM }} => {{ node }}Kind::{{ kind }}({{ kind }}::cast(self.syntax.clone()).unwrap()),
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% else %}
|
{% else %}
|
||||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct {{ node }} {
|
pub struct {{ node }} {
|
||||||
pub(crate) syntax: SyntaxNode,
|
pub(crate) syntax: SyntaxNode,
|
||||||
}
|
}
|
||||||
unsafe impl TransparentNewType for {{ node }} {
|
|
||||||
type Repr = rowan::SyntaxNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AstNode for {{ node }} {
|
impl AstNode for {{ node }} {
|
||||||
fn cast(syntax: &SyntaxNode) -> Option<&Self> {
|
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||||
match syntax.kind() {
|
match syntax.kind() {
|
||||||
{{ node | SCREAM }} => Some({{ node }}::from_repr(syntax.into_repr())),
|
{{ node | SCREAM }} => Some({{ node }} { syntax }),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToOwned for {{ node }} {
|
|
||||||
type Owned = TreeArc<{{ node }}>;
|
|
||||||
fn to_owned(&self) -> TreeArc<{{ node }}> { TreeArc::cast(self.syntax.to_owned()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if methods.traits -%}
|
{% if methods.traits -%}
|
||||||
|
|
||||||
@ -113,7 +92,7 @@ impl {{ node }} {
|
|||||||
{%- for m in methods.collections -%}
|
{%- for m in methods.collections -%}
|
||||||
{%- set method_name = m.0 -%}
|
{%- set method_name = m.0 -%}
|
||||||
{%- set ChildName = m.1 %}
|
{%- set ChildName = m.1 %}
|
||||||
pub fn {{ method_name }}(&self) -> impl Iterator<Item = &{{ ChildName }}> {
|
pub fn {{ method_name }}(&self) -> impl Iterator<Item = {{ ChildName }}> {
|
||||||
super::children(self)
|
super::children(self)
|
||||||
}
|
}
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
@ -129,7 +108,7 @@ impl {{ node }} {
|
|||||||
{%- set method_name = m.0 -%}
|
{%- set method_name = m.0 -%}
|
||||||
{%- set ChildName = m.1 %}
|
{%- set ChildName = m.1 %}
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
pub fn {{ method_name }}(&self) -> Option<&{{ ChildName }}> {
|
pub fn {{ method_name }}(&self) -> Option<{{ ChildName }}> {
|
||||||
super::child_opt(self)
|
super::child_opt(self)
|
||||||
}
|
}
|
||||||
{% endfor -%}
|
{% endfor -%}
|
||||||
|
@ -6,23 +6,23 @@ use crate::{
|
|||||||
SyntaxToken,
|
SyntaxToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Comment<'a>(SyntaxToken<'a>);
|
pub struct Comment(SyntaxToken);
|
||||||
|
|
||||||
impl<'a> AstToken<'a> for Comment<'a> {
|
impl AstToken for Comment {
|
||||||
fn cast(token: SyntaxToken<'a>) -> Option<Self> {
|
fn cast(token: SyntaxToken) -> Option<Self> {
|
||||||
if token.kind() == COMMENT {
|
if token.kind() == COMMENT {
|
||||||
Some(Comment(token))
|
Some(Comment(token))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn syntax(&self) -> SyntaxToken<'a> {
|
fn syntax(&self) -> &SyntaxToken {
|
||||||
self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Comment<'a> {
|
impl Comment {
|
||||||
pub fn kind(&self) -> CommentKind {
|
pub fn kind(&self) -> CommentKind {
|
||||||
kind_by_prefix(self.text())
|
kind_by_prefix(self.text())
|
||||||
}
|
}
|
||||||
@ -90,22 +90,22 @@ fn prefix_by_kind(kind: CommentKind) -> &'static str {
|
|||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Whitespace<'a>(SyntaxToken<'a>);
|
pub struct Whitespace(SyntaxToken);
|
||||||
|
|
||||||
impl<'a> AstToken<'a> for Whitespace<'a> {
|
impl AstToken for Whitespace {
|
||||||
fn cast(token: SyntaxToken<'a>) -> Option<Self> {
|
fn cast(token: SyntaxToken) -> Option<Self> {
|
||||||
if token.kind() == WHITESPACE {
|
if token.kind() == WHITESPACE {
|
||||||
Some(Whitespace(token))
|
Some(Whitespace(token))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn syntax(&self) -> SyntaxToken<'a> {
|
fn syntax(&self) -> &SyntaxToken {
|
||||||
self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Whitespace<'a> {
|
impl Whitespace {
|
||||||
pub fn spans_multiple_lines(&self) -> bool {
|
pub fn spans_multiple_lines(&self) -> bool {
|
||||||
let text = self.text();
|
let text = self.text();
|
||||||
text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n'))
|
text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n'))
|
||||||
|
@ -10,37 +10,37 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub trait TypeAscriptionOwner: AstNode {
|
pub trait TypeAscriptionOwner: AstNode {
|
||||||
fn ascribed_type(&self) -> Option<&ast::TypeRef> {
|
fn ascribed_type(&self) -> Option<ast::TypeRef> {
|
||||||
child_opt(self)
|
child_opt(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait NameOwner: AstNode {
|
pub trait NameOwner: AstNode {
|
||||||
fn name(&self) -> Option<&ast::Name> {
|
fn name(&self) -> Option<ast::Name> {
|
||||||
child_opt(self)
|
child_opt(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait VisibilityOwner: AstNode {
|
pub trait VisibilityOwner: AstNode {
|
||||||
fn visibility(&self) -> Option<&ast::Visibility> {
|
fn visibility(&self) -> Option<ast::Visibility> {
|
||||||
child_opt(self)
|
child_opt(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait LoopBodyOwner: AstNode {
|
pub trait LoopBodyOwner: AstNode {
|
||||||
fn loop_body(&self) -> Option<&ast::Block> {
|
fn loop_body(&self) -> Option<ast::Block> {
|
||||||
child_opt(self)
|
child_opt(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TryBlockBodyOwner: AstNode {
|
pub trait TryBlockBodyOwner: AstNode {
|
||||||
fn try_body(&self) -> Option<&ast::Block> {
|
fn try_body(&self) -> Option<ast::Block> {
|
||||||
child_opt(self)
|
child_opt(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ArgListOwner: AstNode {
|
pub trait ArgListOwner: AstNode {
|
||||||
fn arg_list(&self) -> Option<&ast::ArgList> {
|
fn arg_list(&self) -> Option<ast::ArgList> {
|
||||||
child_opt(self)
|
child_opt(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,10 +51,10 @@ pub trait FnDefOwner: AstNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum ItemOrMacro<'a> {
|
pub enum ItemOrMacro {
|
||||||
Item(&'a ast::ModuleItem),
|
Item(ast::ModuleItem),
|
||||||
Macro(&'a ast::MacroCall),
|
Macro(ast::MacroCall),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ModuleItemOwner: AstNode {
|
pub trait ModuleItemOwner: AstNode {
|
||||||
@ -67,14 +67,14 @@ pub trait ModuleItemOwner: AstNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ItemOrMacroIter<'a>(SyntaxNodeChildren<'a>);
|
pub struct ItemOrMacroIter(SyntaxNodeChildren);
|
||||||
|
|
||||||
impl<'a> Iterator for ItemOrMacroIter<'a> {
|
impl Iterator for ItemOrMacroIter {
|
||||||
type Item = ItemOrMacro<'a>;
|
type Item = ItemOrMacro;
|
||||||
fn next(&mut self) -> Option<ItemOrMacro<'a>> {
|
fn next(&mut self) -> Option<ItemOrMacro> {
|
||||||
loop {
|
loop {
|
||||||
let n = self.0.next()?;
|
let n = self.0.next()?;
|
||||||
if let Some(item) = ast::ModuleItem::cast(n) {
|
if let Some(item) = ast::ModuleItem::cast(n.clone()) {
|
||||||
return Some(ItemOrMacro::Item(item));
|
return Some(ItemOrMacro::Item(item));
|
||||||
}
|
}
|
||||||
if let Some(call) = ast::MacroCall::cast(n) {
|
if let Some(call) = ast::MacroCall::cast(n) {
|
||||||
@ -85,17 +85,17 @@ impl<'a> Iterator for ItemOrMacroIter<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait TypeParamsOwner: AstNode {
|
pub trait TypeParamsOwner: AstNode {
|
||||||
fn type_param_list(&self) -> Option<&ast::TypeParamList> {
|
fn type_param_list(&self) -> Option<ast::TypeParamList> {
|
||||||
child_opt(self)
|
child_opt(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn where_clause(&self) -> Option<&ast::WhereClause> {
|
fn where_clause(&self) -> Option<ast::WhereClause> {
|
||||||
child_opt(self)
|
child_opt(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TypeBoundsOwner: AstNode {
|
pub trait TypeBoundsOwner: AstNode {
|
||||||
fn type_bound_list(&self) -> Option<&ast::TypeBoundList> {
|
fn type_bound_list(&self) -> Option<ast::TypeBoundList> {
|
||||||
child_opt(self)
|
child_opt(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,19 +148,19 @@ pub trait DocCommentsOwner: AstNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CommentIter<'a> {
|
pub struct CommentIter {
|
||||||
iter: SyntaxElementChildren<'a>,
|
iter: SyntaxElementChildren,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for CommentIter<'a> {
|
impl Iterator for CommentIter {
|
||||||
type Item = ast::Comment<'a>;
|
type Item = ast::Comment;
|
||||||
fn next(&mut self) -> Option<ast::Comment<'a>> {
|
fn next(&mut self) -> Option<ast::Comment> {
|
||||||
self.iter.by_ref().find_map(|el| el.as_token().and_then(ast::Comment::cast))
|
self.iter.by_ref().find_map(|el| el.as_token().cloned().and_then(ast::Comment::cast))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DefaultTypeParamOwner: AstNode {
|
pub trait DefaultTypeParamOwner: AstNode {
|
||||||
fn default_type(&self) -> Option<&ast::PathType> {
|
fn default_type(&self) -> Option<ast::PathType> {
|
||||||
child_opt(self)
|
child_opt(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ fn check_file_invariants(file: &SourceFile) {
|
|||||||
|
|
||||||
pub fn check_parser(text: &str) {
|
pub fn check_parser(text: &str) {
|
||||||
let file = SourceFile::parse(text);
|
let file = SourceFile::parse(text);
|
||||||
check_file_invariants(&file.tree);
|
check_file_invariants(&file.tree());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -45,16 +45,16 @@ impl CheckReparse {
|
|||||||
pub fn run(&self) {
|
pub fn run(&self) {
|
||||||
let parse = SourceFile::parse(&self.text);
|
let parse = SourceFile::parse(&self.text);
|
||||||
let new_parse = parse.reparse(&self.edit);
|
let new_parse = parse.reparse(&self.edit);
|
||||||
check_file_invariants(&new_parse.tree);
|
check_file_invariants(&new_parse.tree());
|
||||||
assert_eq!(&new_parse.tree.syntax().text().to_string(), &self.edited_text);
|
assert_eq!(&new_parse.tree().syntax().text().to_string(), &self.edited_text);
|
||||||
let full_reparse = SourceFile::parse(&self.edited_text);
|
let full_reparse = SourceFile::parse(&self.edited_text);
|
||||||
for (a, b) in
|
for (a, b) in
|
||||||
new_parse.tree.syntax().descendants().zip(full_reparse.tree.syntax().descendants())
|
new_parse.tree().syntax().descendants().zip(full_reparse.tree().syntax().descendants())
|
||||||
{
|
{
|
||||||
if (a.kind(), a.range()) != (b.kind(), b.range()) {
|
if (a.kind(), a.range()) != (b.kind(), b.range()) {
|
||||||
eprint!("original:\n{}", parse.tree.syntax().debug_dump());
|
eprint!("original:\n{}", parse.tree().syntax().debug_dump());
|
||||||
eprint!("reparsed:\n{}", new_parse.tree.syntax().debug_dump());
|
eprint!("reparsed:\n{}", new_parse.tree().syntax().debug_dump());
|
||||||
eprint!("full reparse:\n{}", full_reparse.tree.syntax().debug_dump());
|
eprint!("full reparse:\n{}", full_reparse.tree().syntax().debug_dump());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{:?}", a),
|
format!("{:?}", a),
|
||||||
format!("{:?}", b),
|
format!("{:?}", b),
|
||||||
|
@ -31,7 +31,7 @@ pub mod ast;
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub mod fuzz;
|
pub mod fuzz;
|
||||||
|
|
||||||
use std::{fmt::Write, sync::Arc};
|
use std::{fmt::Write, marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
use ra_text_edit::AtomTextEdit;
|
use ra_text_edit::AtomTextEdit;
|
||||||
|
|
||||||
@ -43,8 +43,8 @@ pub use crate::{
|
|||||||
ptr::{AstPtr, SyntaxNodePtr},
|
ptr::{AstPtr, SyntaxNodePtr},
|
||||||
syntax_error::{Location, SyntaxError, SyntaxErrorKind},
|
syntax_error::{Location, SyntaxError, SyntaxErrorKind},
|
||||||
syntax_node::{
|
syntax_node::{
|
||||||
Direction, InsertPosition, SyntaxElement, SyntaxNode, SyntaxNodeWrapper, SyntaxToken,
|
Direction, InsertPosition, SyntaxElement, SyntaxNode, SyntaxToken, SyntaxTreeBuilder,
|
||||||
SyntaxTreeBuilder, TreeArc, WalkEvent,
|
WalkEvent,
|
||||||
},
|
},
|
||||||
syntax_text::SyntaxText,
|
syntax_text::SyntaxText,
|
||||||
};
|
};
|
||||||
@ -58,48 +58,63 @@ pub use rowan::{SmolStr, TextRange, TextUnit};
|
|||||||
/// Note that we always produce a syntax tree, even for completely invalid
|
/// Note that we always produce a syntax tree, even for completely invalid
|
||||||
/// files.
|
/// files.
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub struct Parse<T: SyntaxNodeWrapper> {
|
pub struct Parse<T> {
|
||||||
tree: TreeArc<T>,
|
green: GreenNode,
|
||||||
errors: Arc<Vec<SyntaxError>>,
|
errors: Arc<Vec<SyntaxError>>,
|
||||||
|
_ty: PhantomData<fn() -> T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SyntaxNodeWrapper> Clone for Parse<T> {
|
impl<T> Clone for Parse<T> {
|
||||||
fn clone(&self) -> Parse<T> {
|
fn clone(&self) -> Parse<T> {
|
||||||
Parse { tree: self.tree.clone(), errors: self.errors.clone() }
|
Parse { green: self.green.clone(), errors: self.errors.clone(), _ty: PhantomData }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SyntaxNodeWrapper> Parse<T> {
|
impl<T> Parse<T> {
|
||||||
fn new(tree: TreeArc<T>, errors: Vec<SyntaxError>) -> Parse<T> {
|
fn new(green: GreenNode, errors: Vec<SyntaxError>) -> Parse<T> {
|
||||||
Parse { tree, errors: Arc::new(errors) }
|
Parse { green, errors: Arc::new(errors), _ty: PhantomData }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tree(&self) -> &T {
|
pub fn syntax_node(&self) -> SyntaxNode {
|
||||||
&*self.tree
|
SyntaxNode::new(self.green.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AstNode> Parse<T> {
|
||||||
|
pub fn to_syntax(self) -> Parse<SyntaxNode> {
|
||||||
|
Parse { green: self.green, errors: self.errors, _ty: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tree(&self) -> T {
|
||||||
|
T::cast(self.syntax_node()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn errors(&self) -> &[SyntaxError] {
|
pub fn errors(&self) -> &[SyntaxError] {
|
||||||
&*self.errors
|
&*self.errors
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ok(self) -> Result<TreeArc<T>, Arc<Vec<SyntaxError>>> {
|
pub fn ok(self) -> Result<T, Arc<Vec<SyntaxError>>> {
|
||||||
if self.errors.is_empty() {
|
if self.errors.is_empty() {
|
||||||
Ok(self.tree)
|
Ok(self.tree())
|
||||||
} else {
|
} else {
|
||||||
Err(self.errors)
|
Err(self.errors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AstNode> Parse<T> {
|
impl Parse<SyntaxNode> {
|
||||||
pub fn to_syntax(this: Self) -> Parse<SyntaxNode> {
|
pub fn cast<N: AstNode>(self) -> Option<Parse<N>> {
|
||||||
Parse { tree: this.tree().syntax().to_owned(), errors: this.errors }
|
if N::cast(self.syntax_node()).is_some() {
|
||||||
|
Some(Parse { green: self.green, errors: self.errors, _ty: PhantomData })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse<SourceFile> {
|
impl Parse<SourceFile> {
|
||||||
pub fn debug_dump(&self) -> String {
|
pub fn debug_dump(&self) -> String {
|
||||||
let mut buf = self.tree.syntax().debug_dump();
|
let mut buf = self.tree().syntax().debug_dump();
|
||||||
for err in self.errors.iter() {
|
for err in self.errors.iter() {
|
||||||
writeln!(buf, "error {:?}: {}", err.location(), err.kind()).unwrap();
|
writeln!(buf, "error {:?}: {}", err.location(), err.kind()).unwrap();
|
||||||
}
|
}
|
||||||
@ -112,45 +127,38 @@ impl Parse<SourceFile> {
|
|||||||
|
|
||||||
fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<Parse<SourceFile>> {
|
fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<Parse<SourceFile>> {
|
||||||
// FIXME: validation errors are not handled here
|
// FIXME: validation errors are not handled here
|
||||||
parsing::incremental_reparse(self.tree.syntax(), edit, self.errors.to_vec()).map(
|
parsing::incremental_reparse(self.tree().syntax(), edit, self.errors.to_vec()).map(
|
||||||
|(green_node, errors, _reparsed_range)| Parse {
|
|(green_node, errors, _reparsed_range)| Parse {
|
||||||
tree: SourceFile::new(green_node),
|
green: green_node,
|
||||||
errors: Arc::new(errors),
|
errors: Arc::new(errors),
|
||||||
|
_ty: PhantomData,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn full_reparse(&self, edit: &AtomTextEdit) -> Parse<SourceFile> {
|
fn full_reparse(&self, edit: &AtomTextEdit) -> Parse<SourceFile> {
|
||||||
let text = edit.apply(self.tree.syntax().text().to_string());
|
let text = edit.apply(self.tree().syntax().text().to_string());
|
||||||
SourceFile::parse(&text)
|
SourceFile::parse(&text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse<SyntaxNode> {
|
|
||||||
pub fn cast<T: AstNode>(self) -> Option<Parse<T>> {
|
|
||||||
let node = T::cast(&self.tree)?;
|
|
||||||
Some(Parse { tree: node.to_owned(), errors: self.errors })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `SourceFile` represents a parse tree for a single Rust file.
|
/// `SourceFile` represents a parse tree for a single Rust file.
|
||||||
pub use crate::ast::SourceFile;
|
pub use crate::ast::SourceFile;
|
||||||
|
|
||||||
impl SourceFile {
|
impl SourceFile {
|
||||||
fn new(green: GreenNode) -> TreeArc<SourceFile> {
|
fn new(green: GreenNode) -> SourceFile {
|
||||||
let root = SyntaxNode::new(green);
|
let root = SyntaxNode::new(green);
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
validation::validate_block_structure(&root);
|
validation::validate_block_structure(&root);
|
||||||
}
|
}
|
||||||
assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
|
assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
|
||||||
TreeArc::cast(root)
|
SourceFile::cast(root).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(text: &str) -> Parse<SourceFile> {
|
pub fn parse(text: &str) -> Parse<SourceFile> {
|
||||||
let (green, mut errors) = parsing::parse_text(text);
|
let (green, mut errors) = parsing::parse_text(text);
|
||||||
let tree = SourceFile::new(green);
|
errors.extend(validation::validate(&SourceFile::new(green.clone())));
|
||||||
errors.extend(validation::validate(&tree));
|
Parse { green, errors: Arc::new(errors), _ty: PhantomData }
|
||||||
Parse { tree, errors: Arc::new(errors) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,14 +178,14 @@ fn api_walkthrough() {
|
|||||||
// The `parse` method returns a `Parse` -- a pair of syntax tree and a list
|
// The `parse` method returns a `Parse` -- a pair of syntax tree and a list
|
||||||
// of errors. That is, syntax tree is constructed even in presence of errors.
|
// of errors. That is, syntax tree is constructed even in presence of errors.
|
||||||
let parse = SourceFile::parse(source_code);
|
let parse = SourceFile::parse(source_code);
|
||||||
assert!(parse.errors.is_empty());
|
assert!(parse.errors().is_empty());
|
||||||
|
|
||||||
// Due to the way ownership is set up, owned syntax Nodes always live behind
|
// The `tree` method returns an owned syntax node of type `SourceFile`.
|
||||||
// a `TreeArc` smart pointer. `TreeArc` is roughly an `std::sync::Arc` which
|
// Owned nodes are cheap: inside, they are `Rc` handles to the underling data.
|
||||||
// points to the whole file instead of an individual node.
|
let file: SourceFile = parse.tree();
|
||||||
let file: TreeArc<SourceFile> = parse.tree;
|
|
||||||
|
|
||||||
// `SourceFile` is the root of the syntax tree. We can iterate file's items:
|
// `SourceFile` is the root of the syntax tree. We can iterate file's items.
|
||||||
|
// Let's fetch the `foo` function.
|
||||||
let mut func = None;
|
let mut func = None;
|
||||||
for item in file.items() {
|
for item in file.items() {
|
||||||
match item.kind() {
|
match item.kind() {
|
||||||
@ -185,31 +193,26 @@ fn api_walkthrough() {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The returned items are always references.
|
let func: ast::FnDef = func.unwrap();
|
||||||
let func: &ast::FnDef = func.unwrap();
|
|
||||||
|
|
||||||
// All nodes implement `ToOwned` trait, with `Owned = TreeArc<Self>`.
|
|
||||||
// `to_owned` is a cheap operation: atomic increment.
|
|
||||||
let _owned_func: TreeArc<ast::FnDef> = func.to_owned();
|
|
||||||
|
|
||||||
// Each AST node has a bunch of getters for children. All getters return
|
// Each AST node has a bunch of getters for children. All getters return
|
||||||
// `Option`s though, to account for incomplete code. Some getters are common
|
// `Option`s though, to account for incomplete code. Some getters are common
|
||||||
// for several kinds of node. In this case, a trait like `ast::NameOwner`
|
// for several kinds of node. In this case, a trait like `ast::NameOwner`
|
||||||
// usually exists. By convention, all ast types should be used with `ast::`
|
// usually exists. By convention, all ast types should be used with `ast::`
|
||||||
// qualifier.
|
// qualifier.
|
||||||
let name: Option<&ast::Name> = func.name();
|
let name: Option<ast::Name> = func.name();
|
||||||
let name = name.unwrap();
|
let name = name.unwrap();
|
||||||
assert_eq!(name.text(), "foo");
|
assert_eq!(name.text(), "foo");
|
||||||
|
|
||||||
// Let's get the `1 + 1` expression!
|
// Let's get the `1 + 1` expression!
|
||||||
let block: &ast::Block = func.body().unwrap();
|
let block: ast::Block = func.body().unwrap();
|
||||||
let expr: &ast::Expr = block.expr().unwrap();
|
let expr: ast::Expr = block.expr().unwrap();
|
||||||
|
|
||||||
// "Enum"-like nodes are represented using the "kind" pattern. It allows us
|
// "Enum"-like nodes are represented using the "kind" pattern. It allows us
|
||||||
// to match exhaustively against all flavors of nodes, while maintaining
|
// to match exhaustively against all flavors of nodes, while maintaining
|
||||||
// internal representation flexibility. The drawback is that one can't write
|
// internal representation flexibility. The drawback is that one can't write
|
||||||
// nested matches as one pattern.
|
// nested matches as one pattern.
|
||||||
let bin_expr: &ast::BinExpr = match expr.kind() {
|
let bin_expr: ast::BinExpr = match expr.kind() {
|
||||||
ast::ExprKind::BinExpr(e) => e,
|
ast::ExprKind::BinExpr(e) => e,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
@ -219,23 +222,14 @@ fn api_walkthrough() {
|
|||||||
let expr_syntax: &SyntaxNode = expr.syntax();
|
let expr_syntax: &SyntaxNode = expr.syntax();
|
||||||
|
|
||||||
// Note how `expr` and `bin_expr` are in fact the same node underneath:
|
// Note how `expr` and `bin_expr` are in fact the same node underneath:
|
||||||
assert!(std::ptr::eq(expr_syntax, bin_expr.syntax()));
|
assert!(expr_syntax == bin_expr.syntax());
|
||||||
|
|
||||||
// To go from CST to AST, `AstNode::cast` function is used:
|
// To go from CST to AST, `AstNode::cast` function is used:
|
||||||
let expr = match ast::Expr::cast(expr_syntax) {
|
let _expr: ast::Expr = match ast::Expr::cast(expr_syntax.clone()) {
|
||||||
Some(e) => e,
|
Some(e) => e,
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Note how expr is also a reference!
|
|
||||||
let expr: &ast::Expr = expr;
|
|
||||||
|
|
||||||
// This is possible because the underlying representation is the same:
|
|
||||||
assert_eq!(
|
|
||||||
expr as *const ast::Expr as *const u8,
|
|
||||||
expr_syntax as *const SyntaxNode as *const u8
|
|
||||||
);
|
|
||||||
|
|
||||||
// The two properties each syntax node has is a `SyntaxKind`:
|
// The two properties each syntax node has is a `SyntaxKind`:
|
||||||
assert_eq!(expr_syntax.kind(), SyntaxKind::BIN_EXPR);
|
assert_eq!(expr_syntax.kind(), SyntaxKind::BIN_EXPR);
|
||||||
|
|
||||||
@ -248,7 +242,7 @@ fn api_walkthrough() {
|
|||||||
assert_eq!(text.to_string(), "1 + 1");
|
assert_eq!(text.to_string(), "1 + 1");
|
||||||
|
|
||||||
// There's a bunch of traversal methods on `SyntaxNode`:
|
// There's a bunch of traversal methods on `SyntaxNode`:
|
||||||
assert_eq!(expr_syntax.parent(), Some(block.syntax()));
|
assert_eq!(expr_syntax.parent().as_ref(), Some(block.syntax()));
|
||||||
assert_eq!(block.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{']));
|
assert_eq!(block.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{']));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expr_syntax.next_sibling_or_token().map(|it| it.kind()),
|
expr_syntax.next_sibling_or_token().map(|it| it.kind()),
|
||||||
@ -257,7 +251,7 @@ fn api_walkthrough() {
|
|||||||
|
|
||||||
// As well as some iterator helpers:
|
// As well as some iterator helpers:
|
||||||
let f = expr_syntax.ancestors().find_map(ast::FnDef::cast);
|
let f = expr_syntax.ancestors().find_map(ast::FnDef::cast);
|
||||||
assert_eq!(f, Some(&*func));
|
assert_eq!(f, Some(func));
|
||||||
assert!(expr_syntax.siblings_with_tokens(Direction::Next).any(|it| it.kind() == T!['}']));
|
assert!(expr_syntax.siblings_with_tokens(Direction::Next).any(|it| it.kind() == T!['}']));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
expr_syntax.descendants_with_tokens().count(),
|
expr_syntax.descendants_with_tokens().count(),
|
||||||
@ -272,7 +266,7 @@ fn api_walkthrough() {
|
|||||||
for event in expr_syntax.preorder_with_tokens() {
|
for event in expr_syntax.preorder_with_tokens() {
|
||||||
match event {
|
match event {
|
||||||
WalkEvent::Enter(node) => {
|
WalkEvent::Enter(node) => {
|
||||||
let text = match node {
|
let text = match &node {
|
||||||
SyntaxElement::Node(it) => it.text().to_string(),
|
SyntaxElement::Node(it) => it.text().to_string(),
|
||||||
SyntaxElement::Token(it) => it.text().to_string(),
|
SyntaxElement::Token(it) => it.text().to_string(),
|
||||||
};
|
};
|
||||||
@ -319,7 +313,7 @@ fn api_walkthrough() {
|
|||||||
let mut exprs_visit = Vec::new();
|
let mut exprs_visit = Vec::new();
|
||||||
for node in file.syntax().descendants() {
|
for node in file.syntax().descendants() {
|
||||||
if let Some(result) =
|
if let Some(result) =
|
||||||
visitor().visit::<ast::Expr, _>(|expr| expr.syntax().text().to_string()).accept(node)
|
visitor().visit::<ast::Expr, _>(|expr| expr.syntax().text().to_string()).accept(&node)
|
||||||
{
|
{
|
||||||
exprs_visit.push(result);
|
exprs_visit.push(result);
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ fn reparse_token<'node>(
|
|||||||
root: &'node SyntaxNode,
|
root: &'node SyntaxNode,
|
||||||
edit: &AtomTextEdit,
|
edit: &AtomTextEdit,
|
||||||
) -> Option<(GreenNode, TextRange)> {
|
) -> Option<(GreenNode, TextRange)> {
|
||||||
let token = algo::find_covering_element(root, edit.delete).as_token()?;
|
let token = algo::find_covering_element(root, edit.delete).as_token()?.clone();
|
||||||
match token.kind() {
|
match token.kind() {
|
||||||
WHITESPACE | COMMENT | IDENT | STRING | RAW_STRING => {
|
WHITESPACE | COMMENT | IDENT | STRING | RAW_STRING => {
|
||||||
if token.kind() == WHITESPACE || token.kind() == COMMENT {
|
if token.kind() == WHITESPACE || token.kind() == COMMENT {
|
||||||
@ -51,7 +51,7 @@ fn reparse_token<'node>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let text = get_text_after_edit(token.into(), &edit);
|
let text = get_text_after_edit(token.clone().into(), &edit);
|
||||||
let lex_tokens = tokenize(&text);
|
let lex_tokens = tokenize(&text);
|
||||||
let lex_token = match lex_tokens[..] {
|
let lex_token = match lex_tokens[..] {
|
||||||
[lex_token] if lex_token.kind == token.kind() => lex_token,
|
[lex_token] if lex_token.kind == token.kind() => lex_token,
|
||||||
@ -81,7 +81,7 @@ fn reparse_block<'node>(
|
|||||||
edit: &AtomTextEdit,
|
edit: &AtomTextEdit,
|
||||||
) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
|
) -> Option<(GreenNode, Vec<SyntaxError>, TextRange)> {
|
||||||
let (node, reparser) = find_reparsable_node(root, edit.delete)?;
|
let (node, reparser) = find_reparsable_node(root, edit.delete)?;
|
||||||
let text = get_text_after_edit(node.into(), &edit);
|
let text = get_text_after_edit(node.clone().into(), &edit);
|
||||||
let tokens = tokenize(&text);
|
let tokens = tokenize(&text);
|
||||||
if !is_balanced(&tokens) {
|
if !is_balanced(&tokens) {
|
||||||
return None;
|
return None;
|
||||||
@ -109,7 +109,7 @@ fn is_contextual_kw(text: &str) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_reparsable_node(node: &SyntaxNode, range: TextRange) -> Option<(&SyntaxNode, Reparser)> {
|
fn find_reparsable_node(node: &SyntaxNode, range: TextRange) -> Option<(SyntaxNode, Reparser)> {
|
||||||
let node = algo::find_covering_element(node, range);
|
let node = algo::find_covering_element(node, range);
|
||||||
let mut ancestors = match node {
|
let mut ancestors = match node {
|
||||||
SyntaxElement::Token(it) => it.parent().ancestors(),
|
SyntaxElement::Token(it) => it.parent().ancestors(),
|
||||||
@ -167,8 +167,6 @@ fn merge_errors(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use test_utils::{assert_eq_text, extract_range};
|
use test_utils::{assert_eq_text, extract_range};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -180,18 +178,18 @@ mod tests {
|
|||||||
let after = edit.apply(before.clone());
|
let after = edit.apply(before.clone());
|
||||||
|
|
||||||
let fully_reparsed = SourceFile::parse(&after);
|
let fully_reparsed = SourceFile::parse(&after);
|
||||||
let incrementally_reparsed = {
|
let incrementally_reparsed: Parse<SourceFile> = {
|
||||||
let f = SourceFile::parse(&before);
|
let f = SourceFile::parse(&before);
|
||||||
let edit = AtomTextEdit { delete: range, insert: replace_with.to_string() };
|
let edit = AtomTextEdit { delete: range, insert: replace_with.to_string() };
|
||||||
let (green, new_errors, range) =
|
let (green, new_errors, range) =
|
||||||
incremental_reparse(f.tree.syntax(), &edit, f.errors.to_vec()).unwrap();
|
incremental_reparse(f.tree().syntax(), &edit, f.errors.to_vec()).unwrap();
|
||||||
assert_eq!(range.len(), reparsed_len.into(), "reparsed fragment has wrong length");
|
assert_eq!(range.len(), reparsed_len.into(), "reparsed fragment has wrong length");
|
||||||
Parse { tree: SourceFile::new(green), errors: Arc::new(new_errors) }
|
Parse::new(green, new_errors)
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq_text!(
|
assert_eq_text!(
|
||||||
&fully_reparsed.tree.syntax().debug_dump(),
|
&fully_reparsed.tree().syntax().debug_dump(),
|
||||||
&incrementally_reparsed.tree.syntax().debug_dump(),
|
&incrementally_reparsed.tree().syntax().debug_dump(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::{AstNode, SyntaxKind, SyntaxNode, TextRange};
|
|
||||||
use std::{iter::successors, marker::PhantomData};
|
use std::{iter::successors, marker::PhantomData};
|
||||||
|
|
||||||
|
use crate::{AstNode, SyntaxKind, SyntaxNode, TextRange};
|
||||||
|
|
||||||
/// A pointer to a syntax node inside a file. It can be used to remember a
|
/// A pointer to a syntax node inside a file. It can be used to remember a
|
||||||
/// specific node across reparses of the same file.
|
/// specific node across reparses of the same file.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
@ -14,9 +15,9 @@ impl SyntaxNodePtr {
|
|||||||
SyntaxNodePtr { range: node.range(), kind: node.kind() }
|
SyntaxNodePtr { range: node.range(), kind: node.kind() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_node(self, root: &SyntaxNode) -> &SyntaxNode {
|
pub fn to_node(self, root: &SyntaxNode) -> SyntaxNode {
|
||||||
assert!(root.parent().is_none());
|
assert!(root.parent().is_none());
|
||||||
successors(Some(root), |&node| {
|
successors(Some(root.clone()), |node| {
|
||||||
node.children().find(|it| self.range.is_subrange(&it.range()))
|
node.children().find(|it| self.range.is_subrange(&it.range()))
|
||||||
})
|
})
|
||||||
.find(|it| it.range() == self.range && it.kind() == self.kind)
|
.find(|it| it.range() == self.range && it.kind() == self.kind)
|
||||||
@ -51,7 +52,7 @@ impl<N: AstNode> AstPtr<N> {
|
|||||||
AstPtr { raw: SyntaxNodePtr::new(node.syntax()), _ty: PhantomData }
|
AstPtr { raw: SyntaxNodePtr::new(node.syntax()), _ty: PhantomData }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_node(self, root: &SyntaxNode) -> &N {
|
pub fn to_node(self, root: &SyntaxNode) -> N {
|
||||||
let syntax_node = self.raw.to_node(root);
|
let syntax_node = self.raw.to_node(root);
|
||||||
N::cast(syntax_node).unwrap()
|
N::cast(syntax_node).unwrap()
|
||||||
}
|
}
|
||||||
@ -75,5 +76,5 @@ fn test_local_syntax_ptr() {
|
|||||||
let field = file.syntax().descendants().find_map(ast::NamedFieldDef::cast).unwrap();
|
let field = file.syntax().descendants().find_map(ast::NamedFieldDef::cast).unwrap();
|
||||||
let ptr = SyntaxNodePtr::new(field.syntax());
|
let ptr = SyntaxNodePtr::new(field.syntax());
|
||||||
let field_syntax = ptr.to_node(file.syntax());
|
let field_syntax = ptr.to_node(file.syntax());
|
||||||
assert_eq!(field.syntax(), &*field_syntax);
|
assert_eq!(field.syntax(), &field_syntax);
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,13 @@
|
|||||||
//! modules just wraps its API.
|
//! modules just wraps its API.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Borrow,
|
|
||||||
fmt::{self, Write},
|
fmt::{self, Write},
|
||||||
iter::successors,
|
iter::successors,
|
||||||
ops::RangeInclusive,
|
ops::RangeInclusive,
|
||||||
};
|
};
|
||||||
|
|
||||||
use ra_parser::ParseError;
|
use ra_parser::ParseError;
|
||||||
use rowan::{GreenNodeBuilder, TransparentNewType};
|
use rowan::GreenNodeBuilder;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
syntax_error::{SyntaxError, SyntaxErrorKind},
|
syntax_error::{SyntaxError, SyntaxErrorKind},
|
||||||
@ -33,86 +32,8 @@ pub enum InsertPosition<T> {
|
|||||||
After(T),
|
After(T),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Marker trait for CST and AST nodes
|
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||||
pub trait SyntaxNodeWrapper: TransparentNewType<Repr = rowan::SyntaxNode> {}
|
pub struct SyntaxNode(pub(crate) rowan::cursor::SyntaxNode);
|
||||||
impl<T: TransparentNewType<Repr = rowan::SyntaxNode>> SyntaxNodeWrapper for T {}
|
|
||||||
|
|
||||||
/// An owning smart pointer for CST or AST node.
|
|
||||||
#[derive(PartialEq, Eq, Hash)]
|
|
||||||
pub struct TreeArc<T: SyntaxNodeWrapper>(pub(crate) rowan::TreeArc<T>);
|
|
||||||
|
|
||||||
impl<T: SyntaxNodeWrapper> Borrow<T> for TreeArc<T> {
|
|
||||||
fn borrow(&self) -> &T {
|
|
||||||
&*self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> TreeArc<T>
|
|
||||||
where
|
|
||||||
T: SyntaxNodeWrapper,
|
|
||||||
{
|
|
||||||
pub(crate) fn cast<U>(this: TreeArc<T>) -> TreeArc<U>
|
|
||||||
where
|
|
||||||
U: SyntaxNodeWrapper,
|
|
||||||
{
|
|
||||||
TreeArc(rowan::TreeArc::cast(this.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> std::ops::Deref for TreeArc<T>
|
|
||||||
where
|
|
||||||
T: SyntaxNodeWrapper,
|
|
||||||
{
|
|
||||||
type Target = T;
|
|
||||||
fn deref(&self) -> &T {
|
|
||||||
self.0.deref()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> PartialEq<T> for TreeArc<T>
|
|
||||||
where
|
|
||||||
T: SyntaxNodeWrapper,
|
|
||||||
T: PartialEq<T>,
|
|
||||||
{
|
|
||||||
fn eq(&self, other: &T) -> bool {
|
|
||||||
let t: &T = self;
|
|
||||||
t == other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Clone for TreeArc<T>
|
|
||||||
where
|
|
||||||
T: SyntaxNodeWrapper,
|
|
||||||
{
|
|
||||||
fn clone(&self) -> TreeArc<T> {
|
|
||||||
TreeArc(self.0.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> fmt::Debug for TreeArc<T>
|
|
||||||
where
|
|
||||||
T: SyntaxNodeWrapper,
|
|
||||||
T: fmt::Debug,
|
|
||||||
{
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fmt::Debug::fmt(&self.0, fmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Hash)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct SyntaxNode(pub(crate) rowan::SyntaxNode);
|
|
||||||
unsafe impl TransparentNewType for SyntaxNode {
|
|
||||||
type Repr = rowan::SyntaxNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToOwned for SyntaxNode {
|
|
||||||
type Owned = TreeArc<SyntaxNode>;
|
|
||||||
fn to_owned(&self) -> TreeArc<SyntaxNode> {
|
|
||||||
let ptr = TreeArc(self.0.to_owned());
|
|
||||||
TreeArc::cast(ptr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for SyntaxNode {
|
impl fmt::Debug for SyntaxNode {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
@ -133,9 +54,9 @@ pub enum Direction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SyntaxNode {
|
impl SyntaxNode {
|
||||||
pub(crate) fn new(green: GreenNode) -> TreeArc<SyntaxNode> {
|
pub(crate) fn new(green: GreenNode) -> SyntaxNode {
|
||||||
let ptr = TreeArc(rowan::SyntaxNode::new(green, None));
|
let inner = rowan::cursor::SyntaxNode::new_root(green);
|
||||||
TreeArc::cast(ptr)
|
SyntaxNode(inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> SyntaxKind {
|
pub fn kind(&self) -> SyntaxKind {
|
||||||
@ -143,47 +64,47 @@ impl SyntaxNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn range(&self) -> TextRange {
|
pub fn range(&self) -> TextRange {
|
||||||
self.0.range()
|
self.0.text_range()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text(&self) -> SyntaxText {
|
pub fn text(&self) -> SyntaxText {
|
||||||
SyntaxText::new(self)
|
SyntaxText::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parent(&self) -> Option<&SyntaxNode> {
|
pub fn parent(&self) -> Option<SyntaxNode> {
|
||||||
self.0.parent().map(SyntaxNode::from_repr)
|
self.0.parent().map(SyntaxNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn first_child(&self) -> Option<&SyntaxNode> {
|
pub fn first_child(&self) -> Option<SyntaxNode> {
|
||||||
self.0.first_child().map(SyntaxNode::from_repr)
|
self.0.first_child().map(SyntaxNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn first_child_or_token(&self) -> Option<SyntaxElement> {
|
pub fn first_child_or_token(&self) -> Option<SyntaxElement> {
|
||||||
self.0.first_child_or_token().map(SyntaxElement::from)
|
self.0.first_child_or_token().map(SyntaxElement::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn last_child(&self) -> Option<&SyntaxNode> {
|
pub fn last_child(&self) -> Option<SyntaxNode> {
|
||||||
self.0.last_child().map(SyntaxNode::from_repr)
|
self.0.last_child().map(SyntaxNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn last_child_or_token(&self) -> Option<SyntaxElement> {
|
pub fn last_child_or_token(&self) -> Option<SyntaxElement> {
|
||||||
self.0.last_child_or_token().map(SyntaxElement::from)
|
self.0.last_child_or_token().map(SyntaxElement::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_sibling(&self) -> Option<&SyntaxNode> {
|
pub fn next_sibling(&self) -> Option<SyntaxNode> {
|
||||||
self.0.next_sibling().map(SyntaxNode::from_repr)
|
self.0.next_sibling().map(SyntaxNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_sibling_or_token(&self) -> Option<SyntaxElement> {
|
pub fn next_sibling_or_token(&self) -> Option<SyntaxElement> {
|
||||||
self.0.next_sibling_or_token().map(SyntaxElement::from)
|
self.0.next_sibling_or_token().map(SyntaxElement::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prev_sibling(&self) -> Option<&SyntaxNode> {
|
pub fn prev_sibling(&self) -> Option<SyntaxNode> {
|
||||||
self.0.prev_sibling().map(SyntaxNode::from_repr)
|
self.0.prev_sibling().map(SyntaxNode)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElement> {
|
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElement> {
|
||||||
self.0.prev_sibling_or_token().map(SyntaxElement::from)
|
self.0.prev_sibling_or_token().map(SyntaxElement::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn children(&self) -> SyntaxNodeChildren {
|
pub fn children(&self) -> SyntaxNodeChildren {
|
||||||
@ -195,18 +116,18 @@ impl SyntaxNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn first_token(&self) -> Option<SyntaxToken> {
|
pub fn first_token(&self) -> Option<SyntaxToken> {
|
||||||
self.0.first_token().map(SyntaxToken::from)
|
self.0.first_token().map(SyntaxToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn last_token(&self) -> Option<SyntaxToken> {
|
pub fn last_token(&self) -> Option<SyntaxToken> {
|
||||||
self.0.last_token().map(SyntaxToken::from)
|
self.0.last_token().map(SyntaxToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ancestors(&self) -> impl Iterator<Item = &SyntaxNode> {
|
pub fn ancestors(&self) -> impl Iterator<Item = SyntaxNode> {
|
||||||
successors(Some(self), |&node| node.parent())
|
successors(Some(self.clone()), |node| node.parent())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn descendants(&self) -> impl Iterator<Item = &SyntaxNode> {
|
pub fn descendants(&self) -> impl Iterator<Item = SyntaxNode> {
|
||||||
self.preorder().filter_map(|event| match event {
|
self.preorder().filter_map(|event| match event {
|
||||||
WalkEvent::Enter(node) => Some(node),
|
WalkEvent::Enter(node) => Some(node),
|
||||||
WalkEvent::Leave(_) => None,
|
WalkEvent::Leave(_) => None,
|
||||||
@ -220,8 +141,8 @@ impl SyntaxNode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = &SyntaxNode> {
|
pub fn siblings(&self, direction: Direction) -> impl Iterator<Item = SyntaxNode> {
|
||||||
successors(Some(self), move |&node| match direction {
|
successors(Some(self.clone()), move |node| match direction {
|
||||||
Direction::Next => node.next_sibling(),
|
Direction::Next => node.next_sibling(),
|
||||||
Direction::Prev => node.prev_sibling(),
|
Direction::Prev => node.prev_sibling(),
|
||||||
})
|
})
|
||||||
@ -231,29 +152,29 @@ impl SyntaxNode {
|
|||||||
&self,
|
&self,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
) -> impl Iterator<Item = SyntaxElement> {
|
) -> impl Iterator<Item = SyntaxElement> {
|
||||||
let me: SyntaxElement = self.into();
|
let me: SyntaxElement = self.clone().into();
|
||||||
successors(Some(me), move |el| match direction {
|
successors(Some(me), move |el| match direction {
|
||||||
Direction::Next => el.next_sibling_or_token(),
|
Direction::Next => el.next_sibling_or_token(),
|
||||||
Direction::Prev => el.prev_sibling_or_token(),
|
Direction::Prev => el.prev_sibling_or_token(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn preorder(&self) -> impl Iterator<Item = WalkEvent<&SyntaxNode>> {
|
pub fn preorder(&self) -> impl Iterator<Item = WalkEvent<SyntaxNode>> {
|
||||||
self.0.preorder().map(|event| match event {
|
self.0.preorder().map(|event| match event {
|
||||||
WalkEvent::Enter(n) => WalkEvent::Enter(SyntaxNode::from_repr(n)),
|
WalkEvent::Enter(n) => WalkEvent::Enter(SyntaxNode(n)),
|
||||||
WalkEvent::Leave(n) => WalkEvent::Leave(SyntaxNode::from_repr(n)),
|
WalkEvent::Leave(n) => WalkEvent::Leave(SyntaxNode(n)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn preorder_with_tokens(&self) -> impl Iterator<Item = WalkEvent<SyntaxElement>> {
|
pub fn preorder_with_tokens(&self) -> impl Iterator<Item = WalkEvent<SyntaxElement>> {
|
||||||
self.0.preorder_with_tokens().map(|event| match event {
|
self.0.preorder_with_tokens().map(|event| match event {
|
||||||
WalkEvent::Enter(n) => WalkEvent::Enter(n.into()),
|
WalkEvent::Enter(n) => WalkEvent::Enter(SyntaxElement::new(n)),
|
||||||
WalkEvent::Leave(n) => WalkEvent::Leave(n.into()),
|
WalkEvent::Leave(n) => WalkEvent::Leave(SyntaxElement::new(n)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn memory_size_of_subtree(&self) -> usize {
|
pub fn memory_size_of_subtree(&self) -> usize {
|
||||||
self.0.memory_size_of_subtree()
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn debug_dump(&self) -> String {
|
pub fn debug_dump(&self) -> String {
|
||||||
@ -290,11 +211,11 @@ impl SyntaxNode {
|
|||||||
///
|
///
|
||||||
/// This is a type-unsafe low-level editing API, if you need to use it,
|
/// This is a type-unsafe low-level editing API, if you need to use it,
|
||||||
/// prefer to create a type-safe abstraction on top of it instead.
|
/// prefer to create a type-safe abstraction on top of it instead.
|
||||||
pub fn insert_children<'a>(
|
pub fn insert_children(
|
||||||
&self,
|
&self,
|
||||||
position: InsertPosition<SyntaxElement<'_>>,
|
position: InsertPosition<SyntaxElement>,
|
||||||
to_insert: impl Iterator<Item = SyntaxElement<'a>>,
|
to_insert: impl Iterator<Item = SyntaxElement>,
|
||||||
) -> TreeArc<SyntaxNode> {
|
) -> SyntaxNode {
|
||||||
let mut delta = TextUnit::default();
|
let mut delta = TextUnit::default();
|
||||||
let to_insert = to_insert.map(|element| {
|
let to_insert = to_insert.map(|element| {
|
||||||
delta += element.text_len();
|
delta += element.text_len();
|
||||||
@ -303,7 +224,7 @@ impl SyntaxNode {
|
|||||||
|
|
||||||
let old_children = self.0.green().children();
|
let old_children = self.0.green().children();
|
||||||
|
|
||||||
let new_children = match position {
|
let new_children = match &position {
|
||||||
InsertPosition::First => {
|
InsertPosition::First => {
|
||||||
to_insert.chain(old_children.iter().cloned()).collect::<Box<[_]>>()
|
to_insert.chain(old_children.iter().cloned()).collect::<Box<[_]>>()
|
||||||
}
|
}
|
||||||
@ -312,7 +233,7 @@ impl SyntaxNode {
|
|||||||
}
|
}
|
||||||
InsertPosition::Before(anchor) | InsertPosition::After(anchor) => {
|
InsertPosition::Before(anchor) | InsertPosition::After(anchor) => {
|
||||||
let take_anchor = if let InsertPosition::After(_) = position { 1 } else { 0 };
|
let take_anchor = if let InsertPosition::After(_) = position { 1 } else { 0 };
|
||||||
let split_at = self.position_of_child(anchor) + take_anchor;
|
let split_at = self.position_of_child(anchor.clone()) + take_anchor;
|
||||||
let (before, after) = old_children.split_at(split_at);
|
let (before, after) = old_children.split_at(split_at);
|
||||||
before
|
before
|
||||||
.iter()
|
.iter()
|
||||||
@ -330,13 +251,13 @@ impl SyntaxNode {
|
|||||||
///
|
///
|
||||||
/// This is a type-unsafe low-level editing API, if you need to use it,
|
/// This is a type-unsafe low-level editing API, if you need to use it,
|
||||||
/// prefer to create a type-safe abstraction on top of it instead.
|
/// prefer to create a type-safe abstraction on top of it instead.
|
||||||
pub fn replace_children<'a>(
|
pub fn replace_children(
|
||||||
&self,
|
&self,
|
||||||
to_delete: RangeInclusive<SyntaxElement<'_>>,
|
to_delete: RangeInclusive<SyntaxElement>,
|
||||||
to_insert: impl Iterator<Item = SyntaxElement<'a>>,
|
to_insert: impl Iterator<Item = SyntaxElement>,
|
||||||
) -> TreeArc<SyntaxNode> {
|
) -> SyntaxNode {
|
||||||
let start = self.position_of_child(*to_delete.start());
|
let start = self.position_of_child(to_delete.start().clone());
|
||||||
let end = self.position_of_child(*to_delete.end());
|
let end = self.position_of_child(to_delete.end().clone());
|
||||||
let old_children = self.0.green().children();
|
let old_children = self.0.green().children();
|
||||||
|
|
||||||
let new_children = old_children[..start]
|
let new_children = old_children[..start]
|
||||||
@ -348,7 +269,7 @@ impl SyntaxNode {
|
|||||||
self.with_children(new_children)
|
self.with_children(new_children)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_children(&self, new_children: Box<[rowan::GreenElement]>) -> TreeArc<SyntaxNode> {
|
fn with_children(&self, new_children: Box<[rowan::GreenElement]>) -> SyntaxNode {
|
||||||
let len = new_children.iter().map(|it| it.text_len()).sum::<TextUnit>();
|
let len = new_children.iter().map(|it| it.text_len()).sum::<TextUnit>();
|
||||||
let new_node = GreenNode::new(rowan::SyntaxKind(self.kind() as u16), new_children);
|
let new_node = GreenNode::new(rowan::SyntaxKind(self.kind() as u16), new_children);
|
||||||
let new_file_node = self.replace_with(new_node);
|
let new_file_node = self.replace_with(new_node);
|
||||||
@ -364,7 +285,7 @@ impl SyntaxNode {
|
|||||||
fn position_of_child(&self, child: SyntaxElement) -> usize {
|
fn position_of_child(&self, child: SyntaxElement) -> usize {
|
||||||
self.children_with_tokens()
|
self.children_with_tokens()
|
||||||
.position(|it| it == child)
|
.position(|it| it == child)
|
||||||
.expect("elemetn is not a child of current element")
|
.expect("element is not a child of current element")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,11 +298,11 @@ fn to_green_element(element: SyntaxElement) -> rowan::GreenElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct SyntaxToken<'a>(pub(crate) rowan::SyntaxToken<'a>);
|
pub struct SyntaxToken(pub(crate) rowan::cursor::SyntaxToken);
|
||||||
|
|
||||||
//FIXME: always output text
|
//FIXME: always output text
|
||||||
impl<'a> fmt::Debug for SyntaxToken<'a> {
|
impl fmt::Debug for SyntaxToken {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(fmt, "{:?}@{:?}", self.kind(), self.range())?;
|
write!(fmt, "{:?}@{:?}", self.kind(), self.range())?;
|
||||||
if self.text().len() < 25 {
|
if self.text().len() < 25 {
|
||||||
@ -398,60 +319,54 @@ impl<'a> fmt::Debug for SyntaxToken<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> fmt::Display for SyntaxToken<'a> {
|
impl fmt::Display for SyntaxToken {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
fmt::Display::fmt(self.text(), fmt)
|
fmt::Display::fmt(self.text(), fmt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<rowan::SyntaxToken<'a>> for SyntaxToken<'a> {
|
impl SyntaxToken {
|
||||||
fn from(t: rowan::SyntaxToken<'a>) -> Self {
|
|
||||||
SyntaxToken(t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> SyntaxToken<'a> {
|
|
||||||
pub fn kind(&self) -> SyntaxKind {
|
pub fn kind(&self) -> SyntaxKind {
|
||||||
self.0.kind().0.into()
|
self.0.kind().0.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text(&self) -> &'a SmolStr {
|
pub fn text(&self) -> &SmolStr {
|
||||||
self.0.text()
|
self.0.text()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn range(&self) -> TextRange {
|
pub fn range(&self) -> TextRange {
|
||||||
self.0.range()
|
self.0.text_range()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parent(&self) -> &'a SyntaxNode {
|
pub fn parent(&self) -> SyntaxNode {
|
||||||
SyntaxNode::from_repr(self.0.parent())
|
SyntaxNode(self.0.parent())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_sibling_or_token(&self) -> Option<SyntaxElement<'a>> {
|
pub fn next_sibling_or_token(&self) -> Option<SyntaxElement> {
|
||||||
self.0.next_sibling_or_token().map(SyntaxElement::from)
|
self.0.next_sibling_or_token().map(SyntaxElement::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElement<'a>> {
|
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElement> {
|
||||||
self.0.prev_sibling_or_token().map(SyntaxElement::from)
|
self.0.prev_sibling_or_token().map(SyntaxElement::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn siblings_with_tokens(
|
pub fn siblings_with_tokens(
|
||||||
&self,
|
&self,
|
||||||
direction: Direction,
|
direction: Direction,
|
||||||
) -> impl Iterator<Item = SyntaxElement<'a>> {
|
) -> impl Iterator<Item = SyntaxElement> {
|
||||||
let me: SyntaxElement = (*self).into();
|
let me: SyntaxElement = self.clone().into();
|
||||||
successors(Some(me), move |el| match direction {
|
successors(Some(me), move |el| match direction {
|
||||||
Direction::Next => el.next_sibling_or_token(),
|
Direction::Next => el.next_sibling_or_token(),
|
||||||
Direction::Prev => el.prev_sibling_or_token(),
|
Direction::Prev => el.prev_sibling_or_token(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_token(&self) -> Option<SyntaxToken<'a>> {
|
pub fn next_token(&self) -> Option<SyntaxToken> {
|
||||||
self.0.next_token().map(SyntaxToken::from)
|
self.0.next_token().map(SyntaxToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prev_token(&self) -> Option<SyntaxToken<'a>> {
|
pub fn prev_token(&self) -> Option<SyntaxToken> {
|
||||||
self.0.prev_token().map(SyntaxToken::from)
|
self.0.prev_token().map(SyntaxToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn replace_with(&self, new_token: GreenToken) -> GreenNode {
|
pub(crate) fn replace_with(&self, new_token: GreenToken) -> GreenNode {
|
||||||
@ -459,13 +374,25 @@ impl<'a> SyntaxToken<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||||
pub enum SyntaxElement<'a> {
|
pub enum SyntaxElement {
|
||||||
Node(&'a SyntaxNode),
|
Node(SyntaxNode),
|
||||||
Token(SyntaxToken<'a>),
|
Token(SyntaxToken),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> fmt::Display for SyntaxElement<'a> {
|
impl From<SyntaxNode> for SyntaxElement {
|
||||||
|
fn from(node: SyntaxNode) -> Self {
|
||||||
|
SyntaxElement::Node(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<SyntaxToken> for SyntaxElement {
|
||||||
|
fn from(token: SyntaxToken) -> Self {
|
||||||
|
SyntaxElement::Token(token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SyntaxElement {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
SyntaxElement::Node(it) => fmt::Display::fmt(it, fmt),
|
SyntaxElement::Node(it) => fmt::Display::fmt(it, fmt),
|
||||||
@ -474,7 +401,14 @@ impl<'a> fmt::Display for SyntaxElement<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SyntaxElement<'a> {
|
impl SyntaxElement {
|
||||||
|
pub(crate) fn new(el: rowan::cursor::SyntaxElement) -> Self {
|
||||||
|
match el {
|
||||||
|
rowan::cursor::SyntaxElement::Node(it) => SyntaxElement::Node(SyntaxNode(it)),
|
||||||
|
rowan::cursor::SyntaxElement::Token(it) => SyntaxElement::Token(SyntaxToken(it)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn kind(&self) -> SyntaxKind {
|
pub fn kind(&self) -> SyntaxKind {
|
||||||
match self {
|
match self {
|
||||||
SyntaxElement::Node(it) => it.kind(),
|
SyntaxElement::Node(it) => it.kind(),
|
||||||
@ -482,42 +416,49 @@ impl<'a> SyntaxElement<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_node(&self) -> Option<&'a SyntaxNode> {
|
pub fn as_node(&self) -> Option<&SyntaxNode> {
|
||||||
match self {
|
match self {
|
||||||
SyntaxElement::Node(node) => Some(*node),
|
SyntaxElement::Node(node) => Some(node),
|
||||||
SyntaxElement::Token(_) => None,
|
SyntaxElement::Token(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_token(&self) -> Option<SyntaxToken<'a>> {
|
pub fn as_token(&self) -> Option<&SyntaxToken> {
|
||||||
match self {
|
match self {
|
||||||
SyntaxElement::Node(_) => None,
|
SyntaxElement::Node(_) => None,
|
||||||
SyntaxElement::Token(token) => Some(*token),
|
SyntaxElement::Token(token) => Some(token),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_sibling_or_token(&self) -> Option<SyntaxElement<'a>> {
|
pub fn next_sibling_or_token(&self) -> Option<SyntaxElement> {
|
||||||
match self {
|
match self {
|
||||||
SyntaxElement::Node(it) => it.next_sibling_or_token(),
|
SyntaxElement::Node(it) => it.next_sibling_or_token(),
|
||||||
SyntaxElement::Token(it) => it.next_sibling_or_token(),
|
SyntaxElement::Token(it) => it.next_sibling_or_token(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElement<'a>> {
|
pub fn prev_sibling_or_token(&self) -> Option<SyntaxElement> {
|
||||||
match self {
|
match self {
|
||||||
SyntaxElement::Node(it) => it.prev_sibling_or_token(),
|
SyntaxElement::Node(it) => it.prev_sibling_or_token(),
|
||||||
SyntaxElement::Token(it) => it.prev_sibling_or_token(),
|
SyntaxElement::Token(it) => it.prev_sibling_or_token(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ancestors(&self) -> impl Iterator<Item = &'a SyntaxNode> {
|
pub fn ancestors(&self) -> impl Iterator<Item = SyntaxNode> {
|
||||||
match self {
|
match self {
|
||||||
SyntaxElement::Node(it) => it,
|
SyntaxElement::Node(it) => it.clone(),
|
||||||
SyntaxElement::Token(it) => it.parent(),
|
SyntaxElement::Token(it) => it.parent(),
|
||||||
}
|
}
|
||||||
.ancestors()
|
.ancestors()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn range(&self) -> TextRange {
|
||||||
|
match self {
|
||||||
|
SyntaxElement::Node(it) => it.range(),
|
||||||
|
SyntaxElement::Token(it) => it.range(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn text_len(&self) -> TextUnit {
|
fn text_len(&self) -> TextUnit {
|
||||||
match self {
|
match self {
|
||||||
SyntaxElement::Node(node) => node.0.green().text_len(),
|
SyntaxElement::Node(node) => node.0.green().text_len(),
|
||||||
@ -526,55 +467,23 @@ impl<'a> SyntaxElement<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<rowan::SyntaxElement<'a>> for SyntaxElement<'a> {
|
#[derive(Clone, Debug)]
|
||||||
fn from(el: rowan::SyntaxElement<'a>) -> Self {
|
pub struct SyntaxNodeChildren(rowan::cursor::SyntaxNodeChildren);
|
||||||
match el {
|
|
||||||
rowan::SyntaxElement::Node(n) => SyntaxElement::Node(SyntaxNode::from_repr(n)),
|
impl Iterator for SyntaxNodeChildren {
|
||||||
rowan::SyntaxElement::Token(t) => SyntaxElement::Token(t.into()),
|
type Item = SyntaxNode;
|
||||||
}
|
fn next(&mut self) -> Option<SyntaxNode> {
|
||||||
|
self.0.next().map(SyntaxNode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a SyntaxNode> for SyntaxElement<'a> {
|
#[derive(Clone, Debug)]
|
||||||
fn from(node: &'a SyntaxNode) -> SyntaxElement<'a> {
|
pub struct SyntaxElementChildren(rowan::cursor::SyntaxElementChildren);
|
||||||
SyntaxElement::Node(node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<SyntaxToken<'a>> for SyntaxElement<'a> {
|
impl Iterator for SyntaxElementChildren {
|
||||||
fn from(token: SyntaxToken<'a>) -> SyntaxElement<'a> {
|
type Item = SyntaxElement;
|
||||||
SyntaxElement::Token(token)
|
fn next(&mut self) -> Option<SyntaxElement> {
|
||||||
}
|
self.0.next().map(SyntaxElement::new)
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> SyntaxElement<'a> {
|
|
||||||
pub fn range(&self) -> TextRange {
|
|
||||||
match self {
|
|
||||||
SyntaxElement::Node(it) => it.range(),
|
|
||||||
SyntaxElement::Token(it) => it.range(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SyntaxNodeChildren<'a>(rowan::SyntaxNodeChildren<'a>);
|
|
||||||
|
|
||||||
impl<'a> Iterator for SyntaxNodeChildren<'a> {
|
|
||||||
type Item = &'a SyntaxNode;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<&'a SyntaxNode> {
|
|
||||||
self.0.next().map(SyntaxNode::from_repr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct SyntaxElementChildren<'a>(rowan::SyntaxElementChildren<'a>);
|
|
||||||
|
|
||||||
impl<'a> Iterator for SyntaxElementChildren<'a> {
|
|
||||||
type Item = SyntaxElement<'a>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<SyntaxElement<'a>> {
|
|
||||||
self.0.next().map(SyntaxElement::from)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -601,7 +510,7 @@ impl SyntaxTreeBuilder {
|
|||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
crate::validation::validate_block_structure(&node);
|
crate::validation::validate_block_structure(&node);
|
||||||
}
|
}
|
||||||
Parse::new(node, errors)
|
Parse::new(node.0.green().clone(), errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) {
|
pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) {
|
||||||
|
@ -16,29 +16,36 @@ impl<'a> SyntaxText<'a> {
|
|||||||
SyntaxText { node, range: node.range() }
|
SyntaxText { node, range: node.range() }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chunks(&self) -> impl Iterator<Item = &'a str> {
|
pub fn chunks(&self) -> impl Iterator<Item = SmolStr> {
|
||||||
let range = self.range;
|
let range = self.range;
|
||||||
self.node.descendants_with_tokens().filter_map(move |el| match el {
|
self.node.descendants_with_tokens().filter_map(move |el| match el {
|
||||||
SyntaxElement::Token(t) => {
|
SyntaxElement::Token(t) => {
|
||||||
let text = t.text();
|
let text = t.text();
|
||||||
let range = range.intersection(&t.range())?;
|
let range = range.intersection(&t.range())?;
|
||||||
let range = range - t.range().start();
|
let res = if range == t.range() {
|
||||||
Some(&text[range])
|
t.text().clone()
|
||||||
|
} else {
|
||||||
|
let range = range - t.range().start();
|
||||||
|
text[range].into()
|
||||||
|
};
|
||||||
|
Some(res)
|
||||||
}
|
}
|
||||||
SyntaxElement::Node(_) => None,
|
SyntaxElement::Node(_) => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_to(&self, buf: &mut String) {
|
pub fn push_to(&self, buf: &mut String) {
|
||||||
self.chunks().for_each(|it| buf.push_str(it));
|
self.chunks().for_each(|it| buf.push_str(it.as_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_string(&self) -> String {
|
pub fn to_string(&self) -> String {
|
||||||
self.chunks().collect()
|
let mut buf = String::new();
|
||||||
|
self.push_to(&mut buf);
|
||||||
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_smol_string(&self) -> SmolStr {
|
pub fn to_smol_string(&self) -> SmolStr {
|
||||||
self.chunks().collect()
|
self.to_string().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains(&self, c: char) -> bool {
|
pub fn contains(&self, c: char) -> bool {
|
||||||
@ -52,7 +59,7 @@ impl<'a> SyntaxText<'a> {
|
|||||||
let pos: TextUnit = (pos as u32).into();
|
let pos: TextUnit = (pos as u32).into();
|
||||||
return Some(acc + pos);
|
return Some(acc + pos);
|
||||||
}
|
}
|
||||||
acc += TextUnit::of_str(chunk);
|
acc += TextUnit::of_str(chunk.as_str());
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -97,7 +104,7 @@ impl<'a> SyntaxText<'a> {
|
|||||||
let mut start: TextUnit = 0.into();
|
let mut start: TextUnit = 0.into();
|
||||||
let offset = offset.into();
|
let offset = offset.into();
|
||||||
for chunk in self.chunks() {
|
for chunk in self.chunks() {
|
||||||
let end = start + TextUnit::of_str(chunk);
|
let end = start + TextUnit::of_str(chunk.as_str());
|
||||||
if start <= offset && offset < end {
|
if start <= offset && offset < end {
|
||||||
let off: usize = u32::from(offset - start) as usize;
|
let off: usize = u32::from(offset - start) as usize;
|
||||||
return Some(chunk[off..].chars().next().unwrap());
|
return Some(chunk[off..].chars().next().unwrap());
|
||||||
@ -129,7 +136,7 @@ impl From<SyntaxText<'_>> for String {
|
|||||||
impl PartialEq<str> for SyntaxText<'_> {
|
impl PartialEq<str> for SyntaxText<'_> {
|
||||||
fn eq(&self, mut rhs: &str) -> bool {
|
fn eq(&self, mut rhs: &str) -> bool {
|
||||||
for chunk in self.chunks() {
|
for chunk in self.chunks() {
|
||||||
if !rhs.starts_with(chunk) {
|
if !rhs.starts_with(chunk.as_str()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
rhs = &rhs[chunk.len()..];
|
rhs = &rhs[chunk.len()..];
|
||||||
|
@ -19,13 +19,13 @@ pub(crate) fn validate(file: &SourceFile) -> Vec<SyntaxError> {
|
|||||||
.visit::<ast::Literal, _>(validate_literal)
|
.visit::<ast::Literal, _>(validate_literal)
|
||||||
.visit::<ast::Block, _>(block::validate_block_node)
|
.visit::<ast::Block, _>(block::validate_block_node)
|
||||||
.visit::<ast::FieldExpr, _>(field_expr::validate_field_expr_node)
|
.visit::<ast::FieldExpr, _>(field_expr::validate_field_expr_node)
|
||||||
.accept(node);
|
.accept(&node);
|
||||||
}
|
}
|
||||||
errors
|
errors
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: kill duplication
|
// FIXME: kill duplication
|
||||||
fn validate_literal(literal: &ast::Literal, acc: &mut Vec<SyntaxError>) {
|
fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
|
||||||
let token = literal.token();
|
let token = literal.token();
|
||||||
let text = token.text().as_str();
|
let text = token.text().as_str();
|
||||||
match token.kind() {
|
match token.kind() {
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn validate_block_node(node: &ast::Block, errors: &mut Vec<SyntaxError>) {
|
pub(crate) fn validate_block_node(node: ast::Block, errors: &mut Vec<SyntaxError>) {
|
||||||
if let Some(parent) = node.syntax().parent() {
|
if let Some(parent) = node.syntax().parent() {
|
||||||
match parent.kind() {
|
match parent.kind() {
|
||||||
FN_DEF => return,
|
FN_DEF => return,
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
SyntaxErrorKind::*,
|
SyntaxErrorKind::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) fn validate_field_expr_node(node: &ast::FieldExpr, errors: &mut Vec<SyntaxError>) {
|
pub(crate) fn validate_field_expr_node(node: ast::FieldExpr, errors: &mut Vec<SyntaxError>) {
|
||||||
if let Some(FieldKind::Index(idx)) = node.field_access() {
|
if let Some(FieldKind::Index(idx)) = node.field_access() {
|
||||||
if idx.text().chars().any(|c| c < '0' || c > '9') {
|
if idx.text().chars().any(|c| c < '0' || c > '9') {
|
||||||
errors.push(SyntaxError::new(InvalidTupleIndexFormat, idx.range()));
|
errors.push(SyntaxError::new(InvalidTupleIndexFormat, idx.range()));
|
||||||
|
Loading…
Reference in New Issue
Block a user