Skip to content

Commit

Permalink
feat: lsp completion of pkg, var and schema attr
Browse files Browse the repository at this point in the history
  • Loading branch information
He1pa committed Jul 21, 2023
1 parent 2f6abbd commit 1ba764c
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 106 deletions.
30 changes: 16 additions & 14 deletions kclvm/sema/src/lint/lints_def.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,20 +97,22 @@ impl LintPass for UnusedImport {
let scope_objs = &scope.elems;
for (_, scope_obj) in scope_objs {
let scope_obj = scope_obj.borrow();
if scope_obj.kind == ScopeObjectKind::Module && scope_obj.used == false {
handler.add_warning(
WarningKind::UnusedImportWarning,
&[Message {
pos: Position {
filename: scope_obj.start.filename.clone(),
line: scope_obj.start.line,
column: None,
},
style: Style::Line,
message: format!("Module '{}' imported but unused", scope_obj.name),
note: Some("Consider removing this statement".to_string()),
}],
);
if let ScopeObjectKind::Module(_) = scope_obj.kind {
if !scope_obj.used {
handler.add_warning(
WarningKind::UnusedImportWarning,
&[Message {
pos: Position {
filename: scope_obj.start.filename.clone(),
line: scope_obj.start.line,
column: None,
},
style: Style::Line,
message: format!("Module '{}' imported but unused", scope_obj.name),
note: Some("Consider removing this statement".to_string()),
}],
);
}
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion kclvm/sema/src/resolver/import.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::plugin::PLUGIN_MODULE_PREFIX;
use crate::resolver::scope::Module;
use crate::resolver::Resolver;
use crate::ty::ModuleKind;
use crate::{
Expand Down Expand Up @@ -161,7 +162,12 @@ impl<'ctx> Resolver<'ctx> {
start,
end,
ty: Rc::new(ty),
kind: ScopeObjectKind::Module,
kind: ScopeObjectKind::Module(Module {
path: import_stmt.path.clone(),
rawpath: import_stmt.rawpath.clone(),
name: import_stmt.name.clone(),
asname: import_stmt.asname.clone(),
}),
used: false,
doc: None,
})),
Expand Down
36 changes: 35 additions & 1 deletion kclvm/sema/src/resolver/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,15 @@ pub enum ScopeObjectKind {
Definition,
Parameter,
TypeAlias,
Module,
Module(Module),
}

#[derive(PartialEq, Clone, Debug)]
pub struct Module{
pub path: String,
pub rawpath: String,
pub name: String,
pub asname: Option<String>,
}

/// A Scope maintains a set of objects and links to its containing
Expand Down Expand Up @@ -96,6 +104,22 @@ impl Scope {
}
}

/// Get all usable scope objects in current and parent scope.
pub fn all_usable_objects(&self) -> IndexMap<String, Rc<RefCell<ScopeObject>>> {
let mut res = match &self.parent {
Some(parent) => match parent.upgrade() {
Some(parent) => parent.borrow().all_usable_objects(),
None => IndexMap::new(),
},
None => IndexMap::new(),
};

for (name, obj) in &self.elems {
res.insert(name.clone(), obj.clone());
}
res
}

/// Set a type by name to existed object, return true if found.
pub fn set_ty(&mut self, name: &str, ty: Rc<Type>) -> bool {
match self.elems.get_mut(name) {
Expand Down Expand Up @@ -267,6 +291,16 @@ impl ProgramScope {
};
emit_error().map_err(|e| e.to_string())
}

pub fn inner_most_scope(&self, pos: &Position) -> Option<Scope> {
for (_, scope) in &self.scope_map {
match scope.borrow().inner_most(&pos) {
Some(scope) => return Some(scope),
None => continue,
}
}
None
}
}

/// Construct a builtin scope
Expand Down
2 changes: 1 addition & 1 deletion kclvm/sema/src/resolver/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ fn test_record_used_module() {
.clone();
for (_, obj) in main_scope.elems {
let obj = obj.borrow_mut().clone();
if obj.kind == ScopeObjectKind::Module {
if let ScopeObjectKind::Module(_) = obj.kind {
if obj.name == "math" {
assert!(!obj.used);
} else {
Expand Down
185 changes: 107 additions & 78 deletions kclvm/tools/src/LSP/src/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,19 @@ use std::io;
use std::{fs, path::Path};

use indexmap::IndexSet;
use kclvm_ast::ast::{Expr, ImportStmt, Program, Stmt};
use kclvm_compiler::pkgpath_without_prefix;
use kclvm_ast::ast::{Expr, ImportStmt, Node, Program, SchemaExpr, Stmt};
use kclvm_ast::pos::GetPos;
use kclvm_config::modfile::KCL_FILE_EXTENSION;

use kclvm_error::Position as KCLPos;
use kclvm_sema::builtin::{
get_system_module_members, STANDARD_SYSTEM_MODULES, STRING_MEMBER_FUNCTIONS,
};
use kclvm_sema::resolver::scope::ProgramScope;
use kclvm_sema::resolver::scope::{ProgramScope, ScopeObjectKind};
use lsp_types::CompletionItem;

use crate::goto_def::{get_identifier_last_name, resolve_var};
use crate::goto_def::{find_def, get_identifier_last_name, Definition};
use crate::util::inner_most_expr_in_stmt;
use crate::util::{fix_missing_identifier, get_pkg_scope};

/// Computes completions at the given position.
pub(crate) fn completion(
Expand All @@ -37,8 +36,34 @@ pub(crate) fn completion(
if let Some('.') = trigger_character {
completion_dot(program, pos, prog_scope)
} else {
// todo: Complete identifiers such as attr, variables, types, etc.
None
let mut completions: IndexSet<String> = IndexSet::new();

// complete all usable scope obj in inner_most_scope
if let Some(inner_most_scope) = prog_scope.inner_most_scope(pos) {
for (name, obj) in inner_most_scope.all_usable_objects() {
match &obj.borrow().kind {
kclvm_sema::resolver::scope::ScopeObjectKind::Module(module) => {
completions.insert(module.name.clone());
}
_ => {
completions.insert(name);
}
}
}
}

// complete schema attr
if let Some((node, schema_expr)) = is_in_schema(program, pos) {
let schema_def = find_def(node, &schema_expr.name.get_end_pos(), prog_scope);
if let Some(schema) = schema_def {
if let Definition::Object(obj) = schema {
let schema_type = obj.ty.into_schema_type();
completions.extend(schema_type.attrs.into_keys());
}
}
}

Some(into_completion_items(&completions).into())
}
}

Expand All @@ -57,21 +82,28 @@ fn completion_dot(
match program.pos_to_stmt(pos) {
Some(node) => match node.node {
Stmt::Import(stmt) => completion_for_import(&stmt, pos, prog_scope, program),
_ => {
let expr = inner_most_expr_in_stmt(&node.node, pos, None).0;
match expr {
Some(node) => {
let items = get_completion_items(&node.node, prog_scope);
Some(into_completion_items(&items).into())
}
None => None,
}
}
_ => Some(into_completion_items(&get_completion(node, pos, prog_scope)).into()),
},
None => None,
}
}

fn is_in_schema(program: &Program, pos: &KCLPos) -> Option<(Node<Stmt>, SchemaExpr)> {
match program.pos_to_stmt(pos) {
Some(node) => {
let parent_expr = inner_most_expr_in_stmt(&node.node, pos, None).1;
match parent_expr {
Some(expr) => match expr.node {
Expr::Schema(schema) => Some((node, schema)),
_ => None,
},
None => None,
}
}
None => None,
}
}

fn completion_for_import(
stmt: &ImportStmt,
_pos: &KCLPos,
Expand Down Expand Up @@ -113,83 +145,80 @@ fn completion_for_import(
Some(into_completion_items(&items).into())
}

fn get_completion_items(expr: &Expr, prog_scope: &ProgramScope) -> IndexSet<String> {
let mut items = IndexSet::new();
pub(crate) fn get_completion(
stmt: Node<Stmt>,
pos: &KCLPos,
prog_scope: &ProgramScope,
) -> IndexSet<String> {
let expr = inner_most_expr_in_stmt(&stmt.node, pos, None).0;
match expr {
Expr::Identifier(id) => {
let name = get_identifier_last_name(&id);
if !id.pkgpath.is_empty() {
// standard system module
if STANDARD_SYSTEM_MODULES.contains(&name.as_str()) {
items.extend(
get_system_module_members(name.as_str())
.iter()
.map(|s| s.to_string()),
)
}
// user module
if let Some(scope) = prog_scope
.scope_map
.get(&pkgpath_without_prefix!(id.pkgpath))
{
let scope = scope.borrow();
for (name, obj) in &scope.elems {
if obj.borrow().ty.is_module() {
continue;
Some(node) => {
let mut items = IndexSet::new();
match node.node {
Expr::Identifier(id) => {
let name = get_identifier_last_name(&id);
if !id.pkgpath.is_empty() {
if STANDARD_SYSTEM_MODULES.contains(&name.as_str()) {
items.extend(
get_system_module_members(name.as_str())
.iter()
.map(|s| s.to_string()),
)
}
items.insert(name.clone());
}
}
return items;
}
let def = find_def(stmt, pos, prog_scope);

let def = resolve_var(
&fix_missing_identifier(&id.names),
&get_pkg_scope(&id.pkgpath, &prog_scope.scope_map),
&prog_scope.scope_map,
);

if let Some(def) = def {
match def {
crate::goto_def::Definition::Object(obj) => {
match &obj.ty.kind {
// builtin (str) functions
kclvm_sema::ty::TypeKind::Str => {
let binding = STRING_MEMBER_FUNCTIONS;
for k in binding.keys() {
items.insert(format!("{}{}", k, "()"));
if let Some(def) = def {
match def {
crate::goto_def::Definition::Object(obj) => {
match &obj.ty.kind {
// builtin (str) functions
kclvm_sema::ty::TypeKind::Str => {
let binding = STRING_MEMBER_FUNCTIONS;
for k in binding.keys() {
items.insert(format!("{}{}", k, "()"));
}
}
// schema attrs
kclvm_sema::ty::TypeKind::Schema(schema) => {
for k in schema.attrs.keys() {
if k != "__settings__" {
items.insert(k.clone());
}
}
}
_ => {}
}
}
// schema attrs
kclvm_sema::ty::TypeKind::Schema(schema) => {
for k in schema.attrs.keys() {
if k != "__settings__" {
items.insert(k.clone());
crate::goto_def::Definition::Scope(s) => {
for (name, obj) in &s.elems{
if let ScopeObjectKind::Module(_) = obj.borrow().kind{
continue;
} else {
items.insert(name.clone());
}
}
}
_ => {}
}
}
crate::goto_def::Definition::Scope(_) => {
// todo
}
Expr::Selector(select_expr) => {
let res = get_completion(stmt, &select_expr.value.get_end_pos(), prog_scope);
items.extend(res);
}
Expr::StringLit(_) => {
let binding = STRING_MEMBER_FUNCTIONS;
for k in binding.keys() {
items.insert(format!("{}{}", k, "()"));
}
}
_ => {}
}

items
}
Expr::Selector(select_expr) => {
let res = get_completion_items(&select_expr.value.node, prog_scope);
items.extend(res);
}
Expr::StringLit(_) => {
let binding = STRING_MEMBER_FUNCTIONS;
for k in binding.keys() {
items.insert(format!("{}{}", k, "()"));
}
}
_ => {}
None => IndexSet::new(),
}
items
}

pub(crate) fn into_completion_items(items: &IndexSet<String>) -> Vec<CompletionItem> {
Expand Down
2 changes: 1 addition & 1 deletion kclvm/tools/src/LSP/src/document_symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,6 @@ fn scope_obj_kind_to_document_symbol_kind(kind: ScopeObjectKind) -> SymbolKind {
ScopeObjectKind::Definition => SymbolKind::STRUCT,
ScopeObjectKind::Parameter => SymbolKind::VARIABLE,
ScopeObjectKind::TypeAlias => SymbolKind::TYPE_PARAMETER,
ScopeObjectKind::Module => SymbolKind::MODULE,
ScopeObjectKind::Module(_) => SymbolKind::MODULE,
}
}
Loading

0 comments on commit 1ba764c

Please sign in to comment.