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

feat: better variable destructuring handling & inferring of tpls #537

Merged
merged 10 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/html/symbols/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ pub(crate) fn render_function(
ctx: &RenderContext,
doc_nodes: Vec<&DocNodeWithContext>,
) -> FunctionCtx {
// TODO: this needs to be handled more gracefully on the frontend
let mut overloads_ctx = Vec::with_capacity(doc_nodes.len());
let mut functions_content = Vec::with_capacity(doc_nodes.len());

Expand Down
2 changes: 0 additions & 2 deletions src/html/symbols/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,6 @@ impl SymbolGroupCtx {

split_nodes.sort_keys();

// TODO: property drilldown

let symbols = split_nodes
.values()
.map(|doc_nodes| {
Expand Down
21 changes: 19 additions & 2 deletions src/html/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,25 @@ pub(crate) fn render_type_def(
format!("<span>{:?}</span>", html_escape::encode_safe(&def.repr))
}
LiteralDefKind::Template => {
// TODO(@kitsonk) do this properly and escape properly
format!("<span>`{}`</span>", html_escape::encode_safe(&def.repr))
if let Some(types) = &lit.ts_types {
let mut out = String::new();

for ts_type in types {
out.push_str(&if ts_type
.literal
.as_ref()
.is_some_and(|literal| literal.string.is_some())
{
html_escape::encode_safe(&ts_type.repr).into_owned()
} else {
format!("${{{}}}", render_type_def(ctx, ts_type))
});
}

format!("<span>`{out}`</span>")
} else {
format!("<span>`{}`</span>", html_escape::encode_safe(&def.repr))
}
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -393,14 +393,14 @@ impl<'a> DocParser<'a> {
var_declarator,
)
.into_iter()
.find(|(name, _, _)| name.as_str() == &*ident.sym)
.map(|(name, var_def, _)| {
.find(|(name, _)| name.as_str() == &*ident.sym)
.map(|(name, var_def)| {
let location = get_location(module_info.source(), ident.start());
DocNode::variable(
name,
location,
DeclarationKind::Declare,
js_doc.clone(),
js_doc,
var_def,
)
})
Expand Down
10 changes: 0 additions & 10 deletions src/printer.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

// TODO(ry) This module builds up output by appending to a string. Instead it
// should either use a formatting trait
// https://doc.rust-lang.org/std/fmt/index.html#formatting-traits
// Or perhaps implement a Serializer for serde
// https://docs.serde.rs/serde/ser/trait.Serializer.html

// TODO(ry) The methods in this module take ownership of the DocNodes, this is
// unnecessary and can result in unnecessary copying. Instead they should take
// references.

use crate::colors;
use crate::display::display_abstract;
use crate::display::display_async;
Expand Down
105 changes: 38 additions & 67 deletions src/ts_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ use crate::ParamDef;

use deno_ast::swc::ast::*;
use deno_ast::ParsedSource;
use deno_ast::SourceRange;
use deno_ast::SourceRangedForSpanned;
use serde::Deserialize;
use serde::Serialize;
use std::fmt::Display;
Expand All @@ -30,9 +28,15 @@ impl TsTypeDef {
match &other.lit {
TsLit::Number(num) => TsTypeDef::number_literal(num),
TsLit::Str(str_) => TsTypeDef::string_literal(str_),
TsLit::Tpl(tpl) => {
TsTypeDef::tpl_literal(parsed_source, &tpl.types, &tpl.quasis)
}
TsLit::Tpl(tpl) => TsTypeDef::tpl_literal(
parsed_source,
tpl
.types
.iter()
.map(|types| TsTypeDef::new(parsed_source, types))
.collect::<Vec<_>>(),
&tpl.quasis,
),
TsLit::Bool(bool_) => TsTypeDef::bool_literal(bool_),
TsLit::BigInt(bigint_) => TsTypeDef::bigint_literal(bigint_),
}
Expand Down Expand Up @@ -1128,49 +1132,6 @@ impl Display for TsTypePredicateDef {
}
}

fn get_range_from_type(ts_type: &TsType) -> SourceRange {
use deno_ast::swc::ast::TsType::*;

match ts_type {
TsArrayType(ref t) => get_range_from_type(t.elem_type.as_ref()),
TsConditionalType(ref t) => get_range_from_type(t.check_type.as_ref()),
TsFnOrConstructorType(ref t) => {
if let Some(t) = t.clone().ts_constructor_type() {
t.range()
} else if let Some(t) = t.clone().ts_fn_type() {
t.range()
} else {
unreachable!("no type found")
}
}
TsImportType(ref t) => t.range(),
TsIndexedAccessType(ref t) => get_range_from_type(&t.index_type),
TsInferType(t) => t.range(),
TsKeywordType(t) => t.range(),
TsLitType(t) => t.range(),
TsMappedType(t) => t.range(),
TsOptionalType(t) => t.range(),
TsParenthesizedType(t) => t.range(),
TsRestType(t) => t.range(),
TsThisType(t) => t.range(),
TsTupleType(t) => t.range(),
TsTypeLit(t) => t.range(),
TsTypeOperator(t) => t.range(),
TsTypePredicate(t) => t.range(),
TsTypeQuery(t) => t.range(),
TsTypeRef(t) => t.range(),
TsUnionOrIntersectionType(t) => {
if let Some(t) = t.clone().ts_intersection_type() {
t.range()
} else if let Some(t) = t.clone().ts_union_type() {
t.range()
} else {
unreachable!("no type found")
}
}
}
}

impl TsTypeDef {
pub fn number_literal(num: &Number) -> Self {
Self::number_value(num.value)
Expand Down Expand Up @@ -1205,16 +1166,16 @@ impl TsTypeDef {
}

pub fn tpl_literal(
parsed_source: &ParsedSource,
types: &[Box<TsType>],
_parsed_source: &ParsedSource,
types: Vec<TsTypeDef>,
quasis: &[TplElement],
) -> Self {
let mut ts_types: Vec<(SourceRange, Self, String)> = Vec::new();
let mut types_out: Vec<(Self, String)> = Vec::new();
for ts_type in types {
let t = TsTypeDef::new(parsed_source, ts_type);
let repr = format!("${{{}}}", t);
ts_types.push((get_range_from_type(ts_type), t, repr))
let repr = format!("${{{}}}", ts_type);
types_out.push((ts_type, repr))
}
let mut qasis_out: Vec<(Self, String)> = Vec::new();
for quasi in quasis {
let repr = quasi.raw.to_string();
let lit = LiteralDef {
Expand All @@ -1224,15 +1185,22 @@ impl TsTypeDef {
ts_types: None,
boolean: None,
};
ts_types.push((quasi.range(), Self::literal(repr.clone(), lit), repr));
qasis_out.push((Self::literal(repr.clone(), lit), repr));
}
ts_types.sort_by(|(a, _, _), (b, _, _)| a.cmp(b));
let repr = ts_types
.iter()
.map(|(_, _, s)| s.as_str())
.collect::<Vec<&str>>()
.join("");
let ts_types = Some(ts_types.into_iter().map(|(_, t, _)| t).collect());

let mut out = vec![];

let mut types_iter = types_out.into_iter();

for quasi in qasis_out {
out.push(quasi);
if let Some(ts_type) = types_iter.next() {
out.push(ts_type);
}
}

let repr = out.iter().map(|(_, s)| s.as_str()).collect::<String>();
let ts_types = Some(out.into_iter().map(|(t, _)| t).collect());
let lit = LiteralDef {
kind: LiteralDefKind::Template,
number: None,
Expand Down Expand Up @@ -1815,11 +1783,14 @@ fn infer_ts_type_from_tpl(
tpl: &Tpl,
is_const: bool,
) -> TsTypeDef {
// TODO(@kitsonk) we should iterate over the expr and if each expr has a
// ts_type or can be trivially inferred, it should be passed to the
// tp_literal
if tpl.quasis.len() == 1 && is_const {
TsTypeDef::tpl_literal(parsed_source, &[], &tpl.quasis)
let exprs = tpl
.exprs
.iter()
.map(|expr| infer_ts_type_from_expr(parsed_source, expr, is_const))
.collect::<Option<Vec<_>>>();

if let Some(exprs) = exprs {
TsTypeDef::tpl_literal(parsed_source, exprs, &tpl.quasis)
} else {
TsTypeDef::string_with_repr("string")
}
Expand Down
Loading
Loading