mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 21:54:42 +00:00
move scopes to file
This commit is contained in:
parent
846114a6e9
commit
aaca7d003b
11 changed files with 280 additions and 196 deletions
|
@ -51,6 +51,10 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"commands": [
|
"commands": [
|
||||||
|
{
|
||||||
|
"command": "libsyntax-rust.createFile",
|
||||||
|
"title": "Show Rust syntax tree"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "libsyntax-rust.syntaxTree",
|
"command": "libsyntax-rust.syntaxTree",
|
||||||
"title": "Show Rust syntax tree"
|
"title": "Show Rust syntax tree"
|
||||||
|
|
|
@ -81,11 +81,21 @@ export function activate(context: vscode.ExtensionContext) {
|
||||||
let e = await vscode.window.showTextDocument(doc)
|
let e = await vscode.window.showTextDocument(doc)
|
||||||
e.revealRange(range, vscode.TextEditorRevealType.InCenter)
|
e.revealRange(range, vscode.TextEditorRevealType.InCenter)
|
||||||
})
|
})
|
||||||
console.log("ping")
|
|
||||||
registerCommand('libsyntax-rust.run', async (cmd: ProcessSpec) => {
|
registerCommand('libsyntax-rust.run', async (cmd: ProcessSpec) => {
|
||||||
let task = createTask(cmd)
|
let task = createTask(cmd)
|
||||||
await vscode.tasks.executeTask(task)
|
await vscode.tasks.executeTask(task)
|
||||||
})
|
})
|
||||||
|
registerCommand('libsyntax-rust.createFile', async (uri_: string) => {
|
||||||
|
console.log(`uri: ${uri_}`)
|
||||||
|
let uri = vscode.Uri.parse(uri_)
|
||||||
|
let edit = new vscode.WorkspaceEdit()
|
||||||
|
edit.createFile(uri)
|
||||||
|
await vscode.workspace.applyEdit(edit)
|
||||||
|
let doc = await vscode.workspace.openTextDocument(uri)
|
||||||
|
await vscode.window.showTextDocument(doc)
|
||||||
|
console.log("Done")
|
||||||
|
})
|
||||||
|
|
||||||
dispose(vscode.workspace.registerTextDocumentContentProvider(
|
dispose(vscode.workspace.registerTextDocumentContentProvider(
|
||||||
'libsyntax-rust',
|
'libsyntax-rust',
|
||||||
|
|
|
@ -12,12 +12,9 @@ extern crate rayon;
|
||||||
mod symbol_index;
|
mod symbol_index;
|
||||||
mod module_map;
|
mod module_map;
|
||||||
|
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use rayon::prelude::*;
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
path::{Path},
|
path::{Path, PathBuf},
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
Arc,
|
||||||
atomic::{AtomicBool, Ordering::SeqCst},
|
atomic::{AtomicBool, Ordering::SeqCst},
|
||||||
|
@ -26,13 +23,16 @@ use std::{
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
use libsyntax2::{
|
use libsyntax2::{
|
||||||
File,
|
File,
|
||||||
TextUnit, TextRange, SmolStr,
|
TextUnit, TextRange, SmolStr,
|
||||||
ast::{self, AstNode, NameOwner},
|
ast::{self, AstNode, NameOwner},
|
||||||
SyntaxKind::*,
|
SyntaxKind::*,
|
||||||
};
|
};
|
||||||
use libeditor::{LineIndex, FileSymbol, find_node_at_offset};
|
use libeditor::{Diagnostic, LineIndex, FileSymbol, find_node_at_offset};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
symbol_index::FileSymbols,
|
symbol_index::FileSymbols,
|
||||||
|
@ -130,6 +130,9 @@ impl WorldState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum QuickFix {
|
||||||
|
CreateFile(PathBuf),
|
||||||
|
}
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
pub fn file_syntax(&self, file_id: FileId) -> Result<File> {
|
pub fn file_syntax(&self, file_id: FileId) -> Result<File> {
|
||||||
|
@ -210,6 +213,29 @@ impl World {
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn diagnostics(&self, file_id: FileId) -> Result<Vec<(Diagnostic, Option<QuickFix>)>> {
|
||||||
|
let syntax = self.file_syntax(file_id)?;
|
||||||
|
let mut res = libeditor::diagnostics(&syntax)
|
||||||
|
.into_iter()
|
||||||
|
.map(|d| (d, None))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
for module in syntax.ast().modules() {
|
||||||
|
if module.has_semi() && self.resolve_module(file_id, module).is_empty() {
|
||||||
|
if let Some(name) = module.name() {
|
||||||
|
let d = Diagnostic {
|
||||||
|
range: name.syntax().range(),
|
||||||
|
msg: "unresolved module".to_string(),
|
||||||
|
};
|
||||||
|
let quick_fix = self.data.module_map.suggested_child_mod_path(module)
|
||||||
|
.map(QuickFix::CreateFile);
|
||||||
|
|
||||||
|
res.push((d, quick_fix))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> {
|
fn index_resolve(&self, name_ref: ast::NameRef) -> Vec<(FileId, FileSymbol)> {
|
||||||
let name = name_ref.text();
|
let name = name_ref.text();
|
||||||
let mut query = Query::new(name.to_string());
|
let mut query = Query::new(name.to_string());
|
||||||
|
|
|
@ -93,6 +93,11 @@ impl ModuleMap {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn suggested_child_mod_path(&self, m: ast::Module) -> Option<PathBuf> {
|
||||||
|
let name = m.name()?;
|
||||||
|
Some(PathBuf::from(format!("../{}.rs", name.text())))
|
||||||
|
}
|
||||||
|
|
||||||
fn links(
|
fn links(
|
||||||
&self,
|
&self,
|
||||||
file_resolver: &FileResolver,
|
file_resolver: &FileResolver,
|
||||||
|
|
|
@ -44,6 +44,19 @@ fn test_resolve_module() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unresolved_module_diagnostic() {
|
||||||
|
let mut world = WorldState::new();
|
||||||
|
world.change_file(FileId(1), Some("mod foo;".to_string()));
|
||||||
|
|
||||||
|
let snap = world.snapshot(|_id, _path| None);
|
||||||
|
let diagnostics = snap.diagnostics(FileId(1)).unwrap();
|
||||||
|
assert_eq_dbg(
|
||||||
|
r#"[Diagnostic { range: [4; 7), msg: "unresolved module" }]"#,
|
||||||
|
&diagnostics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_resolve_parent_module() {
|
fn test_resolve_parent_module() {
|
||||||
let mut world = WorldState::new();
|
let mut world = WorldState::new();
|
||||||
|
|
0
crates/libeditor/scope.rs
Normal file
0
crates/libeditor/scope.rs
Normal file
|
@ -1,20 +1,14 @@
|
||||||
use std::{
|
|
||||||
fmt,
|
|
||||||
collections::HashMap,
|
|
||||||
};
|
|
||||||
|
|
||||||
use libsyntax2::{
|
use libsyntax2::{
|
||||||
File, TextUnit, AstNode, SyntaxNodeRef, SyntaxNode, SmolStr,
|
File, TextUnit, AstNode,
|
||||||
ast::{self, NameOwner},
|
ast::self,
|
||||||
algo::{
|
algo::{
|
||||||
ancestors,
|
ancestors,
|
||||||
walk::preorder,
|
|
||||||
generate,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use {
|
use {
|
||||||
AtomEdit, find_node_at_offset,
|
AtomEdit, find_node_at_offset,
|
||||||
|
scope::{FnScopes, compute_scopes},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -43,178 +37,3 @@ fn complete(name_ref: ast::NameRef, scopes: &FnScopes) -> Vec<CompletionItem> {
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_scopes(fn_def: ast::FnDef) -> FnScopes {
|
|
||||||
let mut scopes = FnScopes::new();
|
|
||||||
let root = scopes.root_scope();
|
|
||||||
fn_def.param_list().into_iter()
|
|
||||||
.flat_map(|it| it.params())
|
|
||||||
.filter_map(|it| it.pat())
|
|
||||||
.for_each(|it| scopes.add_bindings(root, it));
|
|
||||||
|
|
||||||
if let Some(body) = fn_def.body() {
|
|
||||||
compute_block_scopes(body, &mut scopes, root)
|
|
||||||
}
|
|
||||||
scopes
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
|
|
||||||
for stmt in block.statements() {
|
|
||||||
match stmt {
|
|
||||||
ast::Stmt::LetStmt(stmt) => {
|
|
||||||
scope = scopes.new_scope(scope);
|
|
||||||
if let Some(pat) = stmt.pat() {
|
|
||||||
scopes.add_bindings(scope, pat);
|
|
||||||
}
|
|
||||||
if let Some(expr) = stmt.initializer() {
|
|
||||||
scopes.set_scope(expr.syntax(), scope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ast::Stmt::ExprStmt(expr_stmt) => {
|
|
||||||
if let Some(expr) = expr_stmt.expr() {
|
|
||||||
scopes.set_scope(expr.syntax(), scope);
|
|
||||||
compute_expr_scopes(expr, scopes, scope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(expr) = block.expr() {
|
|
||||||
scopes.set_scope(expr.syntax(), scope);
|
|
||||||
compute_expr_scopes(expr, scopes, scope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
|
|
||||||
match expr {
|
|
||||||
ast::Expr::IfExpr(e) => {
|
|
||||||
let cond_scope = e.condition().and_then(|cond| {
|
|
||||||
compute_cond_scopes(cond, scopes, scope)
|
|
||||||
});
|
|
||||||
if let Some(block) = e.then_branch() {
|
|
||||||
compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
|
|
||||||
}
|
|
||||||
if let Some(block) = e.else_branch() {
|
|
||||||
compute_block_scopes(block, scopes, scope);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ast::Expr::WhileExpr(e) => {
|
|
||||||
let cond_scope = e.condition().and_then(|cond| {
|
|
||||||
compute_cond_scopes(cond, scopes, scope)
|
|
||||||
});
|
|
||||||
if let Some(block) = e.body() {
|
|
||||||
compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ast::Expr::BlockExpr(e) => {
|
|
||||||
if let Some(block) = e.block() {
|
|
||||||
compute_block_scopes(block, scopes, scope);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ForExpr(e) => TODO,
|
|
||||||
_ => {
|
|
||||||
expr.syntax().children()
|
|
||||||
.filter_map(ast::Expr::cast)
|
|
||||||
.for_each(|expr| compute_expr_scopes(expr, scopes, scope))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fn compute_cond_scopes(cond: ast::Condition, scopes: &mut FnScopes, scope: ScopeId) -> Option<ScopeId> {
|
|
||||||
if let Some(expr) = cond.expr() {
|
|
||||||
compute_expr_scopes(expr, scopes, scope);
|
|
||||||
}
|
|
||||||
if let Some(pat) = cond.pat() {
|
|
||||||
let s = scopes.new_scope(scope);
|
|
||||||
scopes.add_bindings(s, pat);
|
|
||||||
Some(s)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ScopeId = usize;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct FnScopes {
|
|
||||||
scopes: Vec<ScopeData>,
|
|
||||||
scope_for: HashMap<SyntaxNode, ScopeId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FnScopes {
|
|
||||||
fn new() -> FnScopes {
|
|
||||||
FnScopes {
|
|
||||||
scopes: vec![],
|
|
||||||
scope_for: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn root_scope(&mut self) -> ScopeId {
|
|
||||||
let res = self.scopes.len();
|
|
||||||
self.scopes.push(ScopeData { parent: None, entries: vec![] });
|
|
||||||
res
|
|
||||||
}
|
|
||||||
fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
|
|
||||||
let res = self.scopes.len();
|
|
||||||
self.scopes.push(ScopeData { parent: Some(parent), entries: vec![] });
|
|
||||||
res
|
|
||||||
}
|
|
||||||
fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
|
|
||||||
let entries = preorder(pat.syntax())
|
|
||||||
.filter_map(ast::BindPat::cast)
|
|
||||||
.filter_map(ScopeEntry::new);
|
|
||||||
self.scopes[scope].entries.extend(entries);
|
|
||||||
}
|
|
||||||
fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
|
|
||||||
self.scope_for.insert(node.owned(), scope);
|
|
||||||
}
|
|
||||||
fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
|
|
||||||
&self.scopes[scope].entries
|
|
||||||
}
|
|
||||||
fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
|
|
||||||
ancestors(node)
|
|
||||||
.filter_map(|it| self.scope_for.get(&it.owned()).map(|&scope| scope))
|
|
||||||
.next()
|
|
||||||
}
|
|
||||||
fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item=ScopeId> + 'a {
|
|
||||||
generate(self.scope_for(node), move |&scope| self.scopes[scope].parent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct ScopeData {
|
|
||||||
parent: Option<ScopeId>,
|
|
||||||
entries: Vec<ScopeEntry>
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ScopeEntry {
|
|
||||||
syntax: SyntaxNode
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ScopeEntry {
|
|
||||||
fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
|
|
||||||
if pat.name().is_some() {
|
|
||||||
Some(ScopeEntry { syntax: pat.syntax().owned() })
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(&self) -> SmolStr {
|
|
||||||
self.ast().name()
|
|
||||||
.unwrap()
|
|
||||||
.text()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ast(&self) -> ast::BindPat {
|
|
||||||
ast::BindPat::cast(self.syntax.borrowed())
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for ScopeEntry {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
f.debug_struct("ScopeEntry")
|
|
||||||
.field("name", &self.name())
|
|
||||||
.field("syntax", &self.syntax)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ mod edit;
|
||||||
mod code_actions;
|
mod code_actions;
|
||||||
mod typing;
|
mod typing;
|
||||||
mod completion;
|
mod completion;
|
||||||
|
mod scope;
|
||||||
|
|
||||||
use libsyntax2::{
|
use libsyntax2::{
|
||||||
File, TextUnit, TextRange, SyntaxNodeRef,
|
File, TextUnit, TextRange, SyntaxNodeRef,
|
||||||
|
|
183
crates/libeditor/src/scope.rs
Normal file
183
crates/libeditor/src/scope.rs
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
use std::{
|
||||||
|
fmt,
|
||||||
|
collections::HashMap,
|
||||||
|
};
|
||||||
|
|
||||||
|
use libsyntax2::{
|
||||||
|
SyntaxNodeRef, SyntaxNode, SmolStr, AstNode,
|
||||||
|
ast::{self, NameOwner},
|
||||||
|
algo::{ancestors, generate, walk::preorder}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn compute_scopes(fn_def: ast::FnDef) -> FnScopes {
|
||||||
|
let mut scopes = FnScopes::new();
|
||||||
|
let root = scopes.root_scope();
|
||||||
|
fn_def.param_list().into_iter()
|
||||||
|
.flat_map(|it| it.params())
|
||||||
|
.filter_map(|it| it.pat())
|
||||||
|
.for_each(|it| scopes.add_bindings(root, it));
|
||||||
|
|
||||||
|
if let Some(body) = fn_def.body() {
|
||||||
|
compute_block_scopes(body, &mut scopes, root)
|
||||||
|
}
|
||||||
|
scopes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
|
||||||
|
for stmt in block.statements() {
|
||||||
|
match stmt {
|
||||||
|
ast::Stmt::LetStmt(stmt) => {
|
||||||
|
scope = scopes.new_scope(scope);
|
||||||
|
if let Some(pat) = stmt.pat() {
|
||||||
|
scopes.add_bindings(scope, pat);
|
||||||
|
}
|
||||||
|
if let Some(expr) = stmt.initializer() {
|
||||||
|
scopes.set_scope(expr.syntax(), scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ast::Stmt::ExprStmt(expr_stmt) => {
|
||||||
|
if let Some(expr) = expr_stmt.expr() {
|
||||||
|
scopes.set_scope(expr.syntax(), scope);
|
||||||
|
compute_expr_scopes(expr, scopes, scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(expr) = block.expr() {
|
||||||
|
scopes.set_scope(expr.syntax(), scope);
|
||||||
|
compute_expr_scopes(expr, scopes, scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
|
||||||
|
match expr {
|
||||||
|
ast::Expr::IfExpr(e) => {
|
||||||
|
let cond_scope = e.condition().and_then(|cond| {
|
||||||
|
compute_cond_scopes(cond, scopes, scope)
|
||||||
|
});
|
||||||
|
if let Some(block) = e.then_branch() {
|
||||||
|
compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
|
||||||
|
}
|
||||||
|
if let Some(block) = e.else_branch() {
|
||||||
|
compute_block_scopes(block, scopes, scope);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ast::Expr::WhileExpr(e) => {
|
||||||
|
let cond_scope = e.condition().and_then(|cond| {
|
||||||
|
compute_cond_scopes(cond, scopes, scope)
|
||||||
|
});
|
||||||
|
if let Some(block) = e.body() {
|
||||||
|
compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ast::Expr::BlockExpr(e) => {
|
||||||
|
if let Some(block) = e.block() {
|
||||||
|
compute_block_scopes(block, scopes, scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ForExpr(e) => TODO,
|
||||||
|
_ => {
|
||||||
|
expr.syntax().children()
|
||||||
|
.filter_map(ast::Expr::cast)
|
||||||
|
.for_each(|expr| compute_expr_scopes(expr, scopes, scope))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn compute_cond_scopes(cond: ast::Condition, scopes: &mut FnScopes, scope: ScopeId) -> Option<ScopeId> {
|
||||||
|
if let Some(expr) = cond.expr() {
|
||||||
|
compute_expr_scopes(expr, scopes, scope);
|
||||||
|
}
|
||||||
|
if let Some(pat) = cond.pat() {
|
||||||
|
let s = scopes.new_scope(scope);
|
||||||
|
scopes.add_bindings(s, pat);
|
||||||
|
Some(s)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ScopeId = usize;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FnScopes {
|
||||||
|
scopes: Vec<ScopeData>,
|
||||||
|
scope_for: HashMap<SyntaxNode, ScopeId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FnScopes {
|
||||||
|
fn new() -> FnScopes {
|
||||||
|
FnScopes {
|
||||||
|
scopes: vec![],
|
||||||
|
scope_for: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
|
||||||
|
&self.scopes[scope].entries
|
||||||
|
}
|
||||||
|
pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item=ScopeId> + 'a {
|
||||||
|
generate(self.scope_for(node), move |&scope| self.scopes[scope].parent)
|
||||||
|
}
|
||||||
|
fn root_scope(&mut self) -> ScopeId {
|
||||||
|
let res = self.scopes.len();
|
||||||
|
self.scopes.push(ScopeData { parent: None, entries: vec![] });
|
||||||
|
res
|
||||||
|
}
|
||||||
|
fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
|
||||||
|
let res = self.scopes.len();
|
||||||
|
self.scopes.push(ScopeData { parent: Some(parent), entries: vec![] });
|
||||||
|
res
|
||||||
|
}
|
||||||
|
fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
|
||||||
|
let entries = preorder(pat.syntax())
|
||||||
|
.filter_map(ast::BindPat::cast)
|
||||||
|
.filter_map(ScopeEntry::new);
|
||||||
|
self.scopes[scope].entries.extend(entries);
|
||||||
|
}
|
||||||
|
fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
|
||||||
|
self.scope_for.insert(node.owned(), scope);
|
||||||
|
}
|
||||||
|
fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
|
||||||
|
ancestors(node)
|
||||||
|
.filter_map(|it| self.scope_for.get(&it.owned()).map(|&scope| scope))
|
||||||
|
.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ScopeData {
|
||||||
|
parent: Option<ScopeId>,
|
||||||
|
entries: Vec<ScopeEntry>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ScopeEntry {
|
||||||
|
syntax: SyntaxNode
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScopeEntry {
|
||||||
|
fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
|
||||||
|
if pat.name().is_some() {
|
||||||
|
Some(ScopeEntry { syntax: pat.syntax().owned() })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn name(&self) -> SmolStr {
|
||||||
|
self.ast().name()
|
||||||
|
.unwrap()
|
||||||
|
.text()
|
||||||
|
}
|
||||||
|
fn ast(&self) -> ast::BindPat {
|
||||||
|
ast::BindPat::cast(self.syntax.borrowed())
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ScopeEntry {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
f.debug_struct("ScopeEntry")
|
||||||
|
.field("name", &self.name())
|
||||||
|
.field("syntax", &self.syntax)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,8 +29,8 @@ pub(crate) fn literal(p: &mut Parser) -> Option<CompletedMarker> {
|
||||||
pub(super) const ATOM_EXPR_FIRST: TokenSet =
|
pub(super) const ATOM_EXPR_FIRST: TokenSet =
|
||||||
token_set_union![
|
token_set_union![
|
||||||
LITERAL_FIRST,
|
LITERAL_FIRST,
|
||||||
token_set![L_PAREN, PIPE, MOVE_KW, IF_KW, WHILE_KW, MATCH_KW, UNSAFE_KW, L_CURLY, RETURN_KW,
|
token_set![L_CURLY, L_PAREN, L_BRACK, PIPE, MOVE_KW, IF_KW, WHILE_KW, MATCH_KW, UNSAFE_KW,
|
||||||
IDENT, SELF_KW, SUPER_KW, COLONCOLON, BREAK_KW, CONTINUE_KW, LIFETIME ],
|
RETURN_KW, IDENT, SELF_KW, SUPER_KW, COLONCOLON, BREAK_KW, CONTINUE_KW, LIFETIME ],
|
||||||
];
|
];
|
||||||
|
|
||||||
pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> {
|
pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<CompletedMarker> {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use languageserver_types::{
|
||||||
CompletionItem,
|
CompletionItem,
|
||||||
};
|
};
|
||||||
use serde_json::{to_value, from_value};
|
use serde_json::{to_value, from_value};
|
||||||
use libanalysis::{Query};
|
use libanalysis::{Query, QuickFix};
|
||||||
use libeditor;
|
use libeditor;
|
||||||
use libsyntax2::{
|
use libsyntax2::{
|
||||||
TextUnit,
|
TextUnit,
|
||||||
|
@ -177,6 +177,30 @@ pub fn handle_code_action(
|
||||||
};
|
};
|
||||||
res.push(cmd);
|
res.push(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (diag, quick_fix) in world.analysis().diagnostics(file_id)? {
|
||||||
|
let quick_fix = match quick_fix {
|
||||||
|
Some(quick_fix) => quick_fix,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
if !contains_offset_nonstrict(diag.range, offset) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let cmd = match quick_fix {
|
||||||
|
QuickFix::CreateFile(path) => {
|
||||||
|
let path = &path.to_str().unwrap()[3..]; // strip `../` b/c url is weird
|
||||||
|
let uri = params.text_document.uri.join(path)
|
||||||
|
.unwrap();
|
||||||
|
let uri = ::url_serde::Ser::new(&uri);
|
||||||
|
Command {
|
||||||
|
title: "Create file".to_string(),
|
||||||
|
command: "libsyntax-rust.createFile".to_string(),
|
||||||
|
arguments: Some(vec![to_value(uri).unwrap()]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
res.push(cmd)
|
||||||
|
}
|
||||||
return Ok(Some(res));
|
return Ok(Some(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,11 +379,10 @@ pub fn publish_diagnostics(
|
||||||
uri: Url
|
uri: Url
|
||||||
) -> Result<req::PublishDiagnosticsParams> {
|
) -> Result<req::PublishDiagnosticsParams> {
|
||||||
let file_id = world.uri_to_file_id(&uri)?;
|
let file_id = world.uri_to_file_id(&uri)?;
|
||||||
let file = world.analysis().file_syntax(file_id)?;
|
|
||||||
let line_index = world.analysis().file_line_index(file_id)?;
|
let line_index = world.analysis().file_line_index(file_id)?;
|
||||||
let diagnostics = libeditor::diagnostics(&file)
|
let diagnostics = world.analysis().diagnostics(file_id)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|d| Diagnostic {
|
.map(|(d, _quick_fix)| Diagnostic {
|
||||||
range: d.range.conv_with(&line_index),
|
range: d.range.conv_with(&line_index),
|
||||||
severity: Some(DiagnosticSeverity::Error),
|
severity: Some(DiagnosticSeverity::Error),
|
||||||
code: None,
|
code: None,
|
||||||
|
|
Loading…
Reference in a new issue