Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix hover info & go to definition for named parameters #118

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
4 changes: 2 additions & 2 deletions starlark/src/docs/markdown.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,10 +343,10 @@ impl<'a> RenderMarkdown for TypeRenderer<'a> {
DocParam::NoArgs => "*".to_owned(),
DocParam::OnlyPosBefore => "/".to_owned(),
DocParam::Args { typ, name, .. } => {
format!("{}{}", name, raw_type_prefix(": ", typ))
format!("*{}{}", name, raw_type_prefix(": ", typ))
}
DocParam::Kwargs { typ, name, .. } => {
format!("{}{}", name, raw_type_prefix(": ", typ))
format!("**{}{}", name, raw_type_prefix(": ", typ))
}
});

Expand Down
35 changes: 23 additions & 12 deletions starlark/src/docs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,14 +509,21 @@ impl DocFunction {
Some(args) => {
let entries = Self::parse_params(kind, args);
for x in &mut params {
match x {
DocParam::Arg { name, docs, .. }
| DocParam::Args { name, docs, .. }
| DocParam::Kwargs { name, docs, .. } => match entries.get(name) {
Some(raw) => *docs = DocString::from_docstring(kind, raw),
_ => (),
},
_ => (),
if let Some((docs, raw)) = match x {
DocParam::Arg { name, docs, .. } => {
entries.get(name).map(|raw| (docs, raw))
}
DocParam::Args { name, docs, .. } => entries
.get(name)
.or_else(|| entries.get(&format!("*{}", name)))
.map(|raw| (docs, raw)),
DocParam::Kwargs { name, docs, .. } => entries
.get(name)
.or_else(|| entries.get(&format!("**{}", name)))
.map(|raw| (docs, raw)),
_ => None,
} {
*docs = DocString::from_docstring(kind, raw);
}
}
}
Expand Down Expand Up @@ -647,7 +654,7 @@ impl DocParam {
Some(indented)
}

fn render_as_code(&self) -> String {
pub fn render_as_code(&self) -> String {
match self {
DocParam::Arg {
name,
Expand All @@ -662,9 +669,13 @@ impl DocParam {
},
DocParam::NoArgs => "*".to_owned(),
DocParam::OnlyPosBefore => "/".to_owned(),
DocParam::Args { name, typ, .. } | DocParam::Kwargs { name, typ, .. } => match typ {
t if t.is_any() => name.clone(),
typ => format!("{}: {}", name, typ),
DocParam::Args { name, typ, .. } => match typ {
t if t.is_any() => format!("*{}", name),
typ => format!("*{}: {}", name, typ),
},
DocParam::Kwargs { name, typ, .. } => match typ {
t if t.is_any() => format!("**{}", name),
typ => format!("**{}: {}", name, typ),
},
}
}
Expand Down
9 changes: 9 additions & 0 deletions starlark_bin/bin/bazel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ pub(crate) fn main(
prelude: &[PathBuf],
dialect: Dialect,
globals: Globals,
eager: bool,
) -> anyhow::Result<()> {
if !lsp {
return Err(anyhow::anyhow!("Bazel mode only supports `--lsp`"));
Expand All @@ -154,6 +155,7 @@ pub(crate) fn main(
is_interactive,
dialect,
globals,
eager,
)?;

ctx.mode = ContextMode::Check;
Expand All @@ -173,6 +175,7 @@ pub(crate) struct BazelContext {
pub(crate) globals: Globals,
pub(crate) builtin_docs: HashMap<LspUrl, String>,
pub(crate) builtin_symbols: HashMap<String, LspUrl>,
pub(crate) eager: bool,
}

impl BazelContext {
Expand All @@ -187,6 +190,7 @@ impl BazelContext {
module: bool,
dialect: Dialect,
globals: Globals,
eager: bool,
) -> anyhow::Result<Self> {
let prelude: Vec<_> = prelude
.iter()
Expand Down Expand Up @@ -263,6 +267,7 @@ impl BazelContext {
}),
external_output_base: output_base
.map(|output_base| PathBuf::from(output_base).join("external")),
eager,
})
}

Expand Down Expand Up @@ -886,4 +891,8 @@ impl LspContext for BazelContext {

Ok(names)
}

fn is_eager(&self) -> bool {
self.eager
}
}
7 changes: 7 additions & 0 deletions starlark_bin/bin/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ pub(crate) struct Context {
pub(crate) globals: Globals,
pub(crate) builtin_docs: HashMap<LspUrl, String>,
pub(crate) builtin_symbols: HashMap<String, LspUrl>,
pub(crate) eager: bool,
}

/// The outcome of evaluating (checking, parsing or running) given starlark code.
Expand Down Expand Up @@ -101,6 +102,7 @@ impl Context {
module: bool,
dialect: Dialect,
globals: Globals,
eager: bool,
) -> anyhow::Result<Self> {
let prelude: Vec<_> = prelude
.iter()
Expand Down Expand Up @@ -143,6 +145,7 @@ impl Context {
globals,
builtin_docs,
builtin_symbols,
eager,
})
}

Expand Down Expand Up @@ -379,4 +382,8 @@ impl LspContext for Context {
fn get_environment(&self, _uri: &LspUrl) -> DocModule {
DocModule::default()
}

fn is_eager(&self) -> bool {
self.eager
}
}
5 changes: 5 additions & 0 deletions starlark_bin/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ struct Args {
)]
files: Vec<PathBuf>,

#[arg(long = "eager", help = "In LSP mode, eagerly load all files.")]
eager: bool,

#[arg(
long = "bazel",
help = "Run in Bazel mode (temporary, will be removed)"
Expand Down Expand Up @@ -298,6 +301,7 @@ fn main() -> anyhow::Result<()> {
&prelude,
dialect,
globals,
args.eager,
)?;
return Ok(());
}
Expand All @@ -313,6 +317,7 @@ fn main() -> anyhow::Result<()> {
is_interactive,
dialect,
globals,
args.eager,
)?;

if args.lsp {
Expand Down
74 changes: 59 additions & 15 deletions starlark_lsp/src/bind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ use std::collections::HashMap;
use starlark::codemap::Pos;
use starlark::codemap::Span;
use starlark::syntax::AstModule;
use starlark_syntax::syntax::ast::Argument;
use starlark_syntax::syntax::ast::ArgumentP;
use starlark_syntax::syntax::ast::AssignIdentP;
use starlark_syntax::syntax::ast::AssignP;
use starlark_syntax::syntax::ast::AstAssignIdent;
Expand All @@ -33,6 +35,7 @@ use starlark_syntax::syntax::ast::AstTypeExpr;
use starlark_syntax::syntax::ast::Clause;
use starlark_syntax::syntax::ast::DefP;
use starlark_syntax::syntax::ast::Expr;
use starlark_syntax::syntax::ast::ExprP;
use starlark_syntax::syntax::ast::ForClause;
use starlark_syntax::syntax::ast::ForP;
use starlark_syntax::syntax::ast::IdentP;
Expand All @@ -43,21 +46,35 @@ use starlark_syntax::syntax::module::AstModuleFields;
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) enum Assigner {
/// Obtained from `load`. `name` is the symbol in that file, not necessarily the local name
Load {
path: AstString,
name: AstString,
},
Argument, // From a function argument
Assign, // From an assignment
Load { path: AstString, name: AstString },
/// From a function call argument
Argument,
/// From an assignment
Assign,
}

#[derive(Debug)]
pub(crate) enum Bind {
Set(Assigner, AstAssignIdent), // Variable assigned to directly
Get(AstIdent), // Variable that is referenced
GetDotted(GetDotted), // Variable is referenced, but is part of a dotted access
Flow, // Flow control occurs here (if, for etc) - can arrive or leave at this point
Scope(Scope), // Entering a new scope (lambda/def/comprehension)
/// Variable assigned to directly
Set(Assigner, AstAssignIdent),
/// Variable that is referenced
Get(AstIdent),
/// Variable is referenced, but is part of a dotted access
GetDotted(GetDotted),
/// An indirect reference, i.e. a named argument in a function call
IndirectReference(IndirectReference),
/// Flow control occurs here (if, for etc) - can arrive or leave at this point
Flow,
/// Entering a new scope (lambda/def/comprehension)
Scope(Scope),
}

#[derive(Debug)]
pub(crate) struct IndirectReference {
pub(crate) argument_name: AstString,
// TODO: This could also be a dotted access, another function call, etc. These kinds of
// references are not captured at the moment.
pub(crate) function: AstIdent,
}

/// A 'get' bind that was part of a dotted member access pattern.
Expand Down Expand Up @@ -123,7 +140,7 @@ impl Scope {
Bind::Scope(scope) => scope.free.iter().for_each(|(k, v)| {
free.entry(k.clone()).or_insert(*v);
}),
Bind::Flow => {}
Bind::IndirectReference(_) | Bind::Flow => {}
}
}
for x in bound.keys() {
Expand Down Expand Up @@ -183,10 +200,19 @@ fn dot_access<'a>(lhs: &'a AstExpr, attribute: &'a AstString, res: &mut Vec<Bind
attributes.push(attribute);
f(lhs, attributes, res);
}
Expr::Call(name, parameters) => {
f(name, attributes, res);
// make sure that if someone does a(b).c, 'b' is bound and considered used.
Expr::Call(func_name, parameters) => {
f(func_name, attributes, res);
for parameter in parameters {
if let ExprP::Identifier(func_name) = &func_name.node {
if let ArgumentP::Named(arg_name, _) = &parameter.node {
res.push(Bind::IndirectReference(IndirectReference {
argument_name: arg_name.clone(),
function: func_name.clone(),
}))
}
}

// make sure that if someone does a(b).c, 'b' is bound and considered used.
expr(parameter.expr(), res);
}
}
Expand Down Expand Up @@ -221,6 +247,24 @@ fn expr(x: &AstExpr, res: &mut Vec<Bind>) {
expr(&x.0, res);
expr(&x.1, res)
}),
Expr::Call(func, args) => {
expr(func, res);
for x in args {
if let ExprP::Identifier(function_ident) = &func.node {
match &**x {
Argument::Named(name, value) => {
res.push(Bind::IndirectReference(IndirectReference {
argument_name: name.clone(),
function: function_ident.clone(),
}));
expr(value, res);
}
_ => expr(x.expr(), res),
}
}
expr(x.expr(), res)
}
}

// Uninteresting - just recurse
_ => x.visit_expr(|x| expr(x, res)),
Expand Down
27 changes: 12 additions & 15 deletions starlark_lsp/src/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ use lsp_types::TextEdit;
use starlark::codemap::ResolvedSpan;
use starlark::docs::markdown::render_doc_item;
use starlark::docs::markdown::render_doc_param;
use starlark::docs::DocItem;
use starlark::docs::DocMember;
use starlark::docs::DocParam;
use starlark_syntax::codemap::ResolvedPos;
Expand All @@ -42,7 +41,6 @@ use crate::definition::Definition;
use crate::definition::DottedDefinition;
use crate::definition::IdentifierDefinition;
use crate::definition::LspModule;
use crate::exported::SymbolKind as ExportedSymbolKind;
use crate::server::Backend;
use crate::server::LspContext;
use crate::server::LspUrl;
Expand Down Expand Up @@ -96,17 +94,14 @@ impl<T: LspContext> Backend<T> {
(
key,
CompletionItem {
kind: Some(match value.kind {
SymbolKind::Method => CompletionItemKind::METHOD,
SymbolKind::Variable => CompletionItemKind::VARIABLE,
}),
kind: Some(value.kind.into()),
detail: value.detail,
documentation: value
.doc
.map(|doc| {
Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
value: render_doc_item(&value.name, &doc),
value: render_doc_item(&value.name, &doc.to_doc_item()),
})
})
.or_else(|| {
Expand Down Expand Up @@ -258,11 +253,11 @@ impl<T: LspContext> Backend<T> {
)
.remove(name)
.and_then(|symbol| match symbol.kind {
SymbolKind::Method => symbol.doc,
SymbolKind::Variable => None,
SymbolKind::Method { .. } => symbol.doc,
SymbolKind::Constant | SymbolKind::Variable => None,
})
.and_then(|docs| match docs {
DocItem::Function(doc_function) => Some(
DocMember::Function(doc_function) => Some(
doc_function
.params
.into_iter()
Expand All @@ -286,12 +281,12 @@ impl<T: LspContext> Backend<T> {
self.get_ast_or_load_from_disk(&load_uri)?
.and_then(|ast| ast.find_exported_symbol(name))
.and_then(|symbol| match symbol.kind {
ExportedSymbolKind::Any => None,
ExportedSymbolKind::Function { argument_names } => Some(
argument_names
SymbolKind::Constant | SymbolKind::Variable => None,
SymbolKind::Method { arguments } => Some(
arguments
.into_iter()
.map(|name| CompletionItem {
label: name,
.map(|arg| CompletionItem {
label: arg.name,
kind: Some(CompletionItemKind::PROPERTY),
..Default::default()
})
Expand Down Expand Up @@ -330,6 +325,8 @@ impl<T: LspContext> Backend<T> {
}
// None of these can be functions, so can't have any parameters.
IdentifierDefinition::LoadPath { .. }
| IdentifierDefinition::LocationWithParameterReference { .. }
| IdentifierDefinition::LoadedLocationWithParameterReference { .. }
| IdentifierDefinition::StringLiteral { .. }
| IdentifierDefinition::NotFound => None,
})
Expand Down
Loading
Loading