Skip to content

Commit 11ed8c8

Browse files
Fix links for external dependencies
1 parent 789205e commit 11ed8c8

8 files changed

+276
-25
lines changed

compiler-cli/src/docs.rs

+51-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::time::{Instant, SystemTime};
1+
use std::{
2+
collections::HashMap,
3+
time::{Instant, SystemTime},
4+
};
25

36
use camino::{Utf8Path, Utf8PathBuf};
47
use ecow::EcoString;
@@ -9,10 +12,11 @@ use gleam_core::{
912
analyse::TargetSupport,
1013
build::{Codegen, Compile, Mode, Options, Package, Target},
1114
config::{DocsPage, PackageConfig},
12-
docs::DocContext,
15+
docs::{Dependency, DependencyKind, DocContext},
1316
error::Error,
1417
hex,
1518
io::HttpClient as _,
19+
manifest::ManifestPackageSource,
1620
paths::ProjectPaths,
1721
type_,
1822
};
@@ -49,6 +53,26 @@ pub fn build(paths: &ProjectPaths, options: BuildOptions) -> Result<()> {
4953
crate::fs::delete_directory(&paths.build_directory_for_target(Mode::Prod, config.target))?;
5054

5155
let out = paths.build_documentation_directory(&config.name);
56+
57+
let manifest = crate::build::download_dependencies(paths, cli::Reporter::new())?;
58+
let dependencies = manifest
59+
.packages
60+
.iter()
61+
.map(|package| {
62+
(
63+
package.name.clone(),
64+
Dependency {
65+
version: package.version.clone(),
66+
kind: match &package.source {
67+
ManifestPackageSource::Hex { .. } => DependencyKind::Hex,
68+
ManifestPackageSource::Git { .. } => DependencyKind::Git,
69+
ManifestPackageSource::Local { .. } => DependencyKind::Path,
70+
},
71+
},
72+
)
73+
})
74+
.collect();
75+
5276
let mut built = crate::build::main(
5377
paths,
5478
Options {
@@ -60,11 +84,12 @@ pub fn build(paths: &ProjectPaths, options: BuildOptions) -> Result<()> {
6084
root_target_support: TargetSupport::Enforced,
6185
no_print_progress: false,
6286
},
63-
crate::build::download_dependencies(paths, cli::Reporter::new())?,
87+
manifest,
6488
)?;
6589
let outputs = build_documentation(
6690
paths,
6791
&config,
92+
dependencies,
6893
&mut built.root_package,
6994
DocContext::Build,
7095
&built.module_interfaces,
@@ -106,6 +131,7 @@ fn open_docs(path: &Utf8Path) -> Result<()> {
106131
pub(crate) fn build_documentation(
107132
paths: &ProjectPaths,
108133
config: &PackageConfig,
134+
dependencies: HashMap<EcoString, Dependency>,
109135
compiled: &mut Package,
110136
is_hex_publish: DocContext,
111137
cached_modules: &im::HashMap<EcoString, type_::ModuleInterface>,
@@ -121,6 +147,7 @@ pub(crate) fn build_documentation(
121147
let mut outputs = gleam_core::docs::generate_html(
122148
paths,
123149
config,
150+
dependencies,
124151
compiled.modules.as_slice(),
125152
&pages,
126153
ProjectIO::new(),
@@ -147,6 +174,25 @@ pub fn publish(paths: &ProjectPaths) -> Result<()> {
147174
// Reset the build directory so we know the state of the project
148175
crate::fs::delete_directory(&paths.build_directory_for_target(Mode::Prod, config.target))?;
149176

177+
let manifest = crate::build::download_dependencies(paths, cli::Reporter::new())?;
178+
let dependencies = manifest
179+
.packages
180+
.iter()
181+
.map(|package| {
182+
(
183+
package.name.clone(),
184+
Dependency {
185+
version: package.version.clone(),
186+
kind: match &package.source {
187+
ManifestPackageSource::Hex { .. } => DependencyKind::Hex,
188+
ManifestPackageSource::Git { .. } => DependencyKind::Git,
189+
ManifestPackageSource::Local { .. } => DependencyKind::Path,
190+
},
191+
},
192+
)
193+
})
194+
.collect();
195+
150196
let mut built = crate::build::main(
151197
paths,
152198
Options {
@@ -158,11 +204,12 @@ pub fn publish(paths: &ProjectPaths) -> Result<()> {
158204
target: None,
159205
no_print_progress: false,
160206
},
161-
crate::build::download_dependencies(paths, cli::Reporter::new())?,
207+
manifest,
162208
)?;
163209
let outputs = build_documentation(
164210
paths,
165211
&config,
212+
dependencies,
166213
&mut built.root_package,
167214
DocContext::HexPublish,
168215
&built.module_interfaces,

compiler-cli/src/publish.rs

+27-3
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,18 @@ use gleam_core::{
66
analyse::TargetSupport,
77
build::{Codegen, Compile, Mode, Options, Package, Target},
88
config::{GleamVersion, PackageConfig, SpdxLicense},
9-
docs::DocContext,
9+
docs::{Dependency, DependencyKind, DocContext},
1010
error::{SmallVersion, wrap},
1111
hex,
12+
manifest::ManifestPackageSource,
1213
paths::{self, ProjectPaths},
1314
requirement::Requirement,
1415
type_,
1516
};
1617
use hexpm::version::{Range, Version};
1718
use itertools::Itertools;
1819
use sha2::Digest;
19-
use std::{io::Write, path::PathBuf, time::Instant};
20+
use std::{collections::HashMap, io::Write, path::PathBuf, time::Instant};
2021

2122
use crate::{build, cli, docs, fs, http::HttpClient};
2223

@@ -38,6 +39,7 @@ pub fn command(paths: &ProjectPaths, replace: bool, i_am_sure: bool) -> Result<(
3839
data: package_tarball,
3940
src_files_added,
4041
generated_files_added,
42+
dependencies,
4143
} = do_build_hex_tarball(paths, &mut config)?;
4244

4345
check_for_name_squatting(&compile_result)?;
@@ -47,6 +49,7 @@ pub fn command(paths: &ProjectPaths, replace: bool, i_am_sure: bool) -> Result<(
4749
let docs_tarball = fs::create_tar_archive(docs::build_documentation(
4850
paths,
4951
&config,
52+
dependencies,
5053
&mut compile_result,
5154
DocContext::HexPublish,
5255
&cached_modules,
@@ -271,6 +274,7 @@ struct Tarball {
271274
data: Vec<u8>,
272275
src_files_added: Vec<Utf8PathBuf>,
273276
generated_files_added: Vec<(Utf8PathBuf, String)>,
277+
dependencies: HashMap<EcoString, Dependency>,
274278
}
275279

276280
pub fn build_hex_tarball(paths: &ProjectPaths, config: &mut PackageConfig) -> Result<Vec<u8>> {
@@ -285,6 +289,25 @@ fn do_build_hex_tarball(paths: &ProjectPaths, config: &mut PackageConfig) -> Res
285289
// Reset the build directory so we know the state of the project
286290
fs::delete_directory(&paths.build_directory_for_target(Mode::Prod, target))?;
287291

292+
let manifest = build::download_dependencies(paths, cli::Reporter::new())?;
293+
let dependencies = manifest
294+
.packages
295+
.iter()
296+
.map(|package| {
297+
(
298+
package.name.clone(),
299+
Dependency {
300+
version: package.version.clone(),
301+
kind: match &package.source {
302+
ManifestPackageSource::Hex { .. } => DependencyKind::Hex,
303+
ManifestPackageSource::Git { .. } => DependencyKind::Git,
304+
ManifestPackageSource::Local { .. } => DependencyKind::Path,
305+
},
306+
},
307+
)
308+
})
309+
.collect();
310+
288311
// Build the project to check that it is valid
289312
let built = build::main(
290313
paths,
@@ -297,7 +320,7 @@ fn do_build_hex_tarball(paths: &ProjectPaths, config: &mut PackageConfig) -> Res
297320
compile: Compile::All,
298321
no_print_progress: false,
299322
},
300-
build::download_dependencies(paths, cli::Reporter::new())?,
323+
manifest,
301324
)?;
302325

303326
let minimum_required_version = built.minimum_required_version();
@@ -399,6 +422,7 @@ fn do_build_hex_tarball(paths: &ProjectPaths, config: &mut PackageConfig) -> Res
399422
data: tarball,
400423
src_files_added: src_files,
401424
generated_files_added: generated_files,
425+
dependencies,
402426
})
403427
}
404428

compiler-core/src/docs.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ mod source_links;
33
#[cfg(test)]
44
mod tests;
55

6-
use std::time::SystemTime;
6+
use std::{collections::HashMap, time::SystemTime};
77

88
use camino::Utf8PathBuf;
9+
use hexpm::version::Version;
910
use printer::Printer;
1011

1112
use crate::{
@@ -36,9 +37,25 @@ pub struct PackageInformation {
3637
package_config: PackageConfig,
3738
}
3839

40+
/// Like `ManifestPackage`, but lighter and cheaper to clone as it is all that
41+
/// we need for printing documentation.
42+
#[derive(Debug, Clone)]
43+
pub struct Dependency {
44+
pub version: Version,
45+
pub kind: DependencyKind,
46+
}
47+
48+
#[derive(Debug, Clone, Copy)]
49+
pub enum DependencyKind {
50+
Hex,
51+
Path,
52+
Git,
53+
}
54+
3955
pub fn generate_html<IO: FileSystemReader>(
4056
paths: &ProjectPaths,
4157
config: &PackageConfig,
58+
dependencies: HashMap<EcoString, Dependency>,
4259
analysed: &[Module],
4360
docs_pages: &[DocsPage],
4461
fs: IO,
@@ -174,6 +191,7 @@ pub fn generate_html<IO: FileSystemReader>(
174191
module.ast.type_info.package.clone(),
175192
module.name.clone(),
176193
&module.ast.names,
194+
&dependencies,
177195
);
178196

179197
let types: Vec<TypeDefinition<'_>> = module

compiler-core/src/docs/printer.rs

+35-8
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ use crate::{
2020
};
2121

2222
use super::{
23-
DocsValues, TypeConstructor, TypeConstructorArg, TypeDefinition, markdown_documentation,
24-
source_links::SourceLinker, text_documentation,
23+
Dependency, DependencyKind, DocsValues, TypeConstructor, TypeConstructorArg, TypeDefinition,
24+
markdown_documentation, source_links::SourceLinker, text_documentation,
2525
};
2626

2727
#[derive(Clone, Copy)]
@@ -55,10 +55,17 @@ pub struct Printer<'a> {
5555
/// An incrementing number used to generate the next type variable name.
5656
/// `0` becomes `a`, `1` becomes `b`, etc.
5757
next_type_variable_id: u64,
58+
59+
dependencies: &'a HashMap<EcoString, Dependency>,
5860
}
5961

6062
impl Printer<'_> {
61-
pub fn new(package: EcoString, module: EcoString, names: &Names) -> Printer<'_> {
63+
pub fn new<'a>(
64+
package: EcoString,
65+
module: EcoString,
66+
names: &'a Names,
67+
dependencies: &'a HashMap<EcoString, Dependency>,
68+
) -> Printer<'a> {
6269
Printer {
6370
options: PrintOptions::all(),
6471
names,
@@ -67,6 +74,7 @@ impl Printer<'_> {
6774
printed_type_variables: HashMap::new(),
6875
printed_type_variable_names: HashSet::new(),
6976
next_type_variable_id: 0,
77+
dependencies,
7078
}
7179
}
7280

@@ -521,11 +529,19 @@ impl Printer<'_> {
521529
];
522530
let title = eco_format!("{module}.{{type {name}}}");
523531

524-
self.link(
525-
eco_format!("https://hexdocs.pm/{package}/{module}.html#{name}"),
526-
qualified_name,
527-
Some(title),
528-
)
532+
// We can't reliably link to documentation if the type is from a path
533+
// or git dependency
534+
match self.dependencies.get(package) {
535+
Some(Dependency {
536+
kind: DependencyKind::Hex,
537+
version,
538+
}) => self.link(
539+
eco_format!("https://hexdocs.pm/{package}/{version}/{module}.html#{name}"),
540+
qualified_name,
541+
Some(title),
542+
),
543+
Some(_) | None => self.span_with_title(qualified_name, title),
544+
}
529545
}
530546
}
531547

@@ -626,6 +642,17 @@ impl Printer<'_> {
626642
zero_width_string("</a>".into()),
627643
)
628644
}
645+
646+
fn span_with_title<'a>(&self, name: impl Documentable<'a>, title: EcoString) -> Document<'a> {
647+
if !self.options.print_links {
648+
return name.to_doc();
649+
}
650+
651+
name.to_doc().surround(
652+
zero_width_string(eco_format!(r#"<span title="{title}">"#)),
653+
zero_width_string("</span>".into()),
654+
)
655+
}
629656
}
630657

631658
const MAX_COLUMNS: isize = 65;

compiler-core/src/docs/snapshots/gleam_core__docs__tests__link_to_type_in_different_package.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ pub fn make_dict() -> dict.Dict(a, b) { todo }
1616
---- VALUES
1717

1818
--- make_dict
19-
<pre><code>pub fn make_dict() -> <a href="https://hexdocs.pm/gleam_stdlib/gleam/dict.html#Dict" title="gleam/dict.{type Dict}">dict.Dict</a>(a, b)</code></pre>
19+
<pre><code>pub fn make_dict() -> <a href="https://hexdocs.pm/gleam_stdlib/1.0.0/gleam/dict.html#Dict" title="gleam/dict.{type Dict}">dict.Dict</a>(a, b)</code></pre>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
source: compiler-core/src/docs/tests.rs
3+
expression: output
4+
---
5+
---- SOURCE CODE
6+
-- gleam/dict.gleam
7+
pub type Dict(a, b)
8+
9+
-- main.gleam
10+
11+
import gleam/dict
12+
13+
pub fn make_dict() -> dict.Dict(a, b) { todo }
14+
15+
16+
---- VALUES
17+
18+
--- make_dict
19+
<pre><code>pub fn make_dict() -> <span title="gleam/dict.{type Dict}">dict.Dict</span>(a, b)</code></pre>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
source: compiler-core/src/docs/tests.rs
3+
expression: output
4+
---
5+
---- SOURCE CODE
6+
-- gleam/dict.gleam
7+
pub type Dict(a, b)
8+
9+
-- main.gleam
10+
11+
import gleam/dict
12+
13+
pub fn make_dict() -> dict.Dict(a, b) { todo }
14+
15+
16+
---- VALUES
17+
18+
--- make_dict
19+
<pre><code>pub fn make_dict() -> <span title="gleam/dict.{type Dict}">dict.Dict</span>(a, b)</code></pre>

0 commit comments

Comments
 (0)