-

{{name}}

+
+

{{name}}

+ {{#if @root.github_source}} +
+ [src] +
+ {{/if}} +
{{#if sub_modules}}

Modules

@@ -53,10 +60,22 @@

Modules

Types

{{#each record.types}} -

-
type {{name}}{{#each args}} {{name}}{{/each}} = {{{type~}}}
-                    
-

+
+
+

+
type {{name}}{{#each args}} {{name}}{{/each}} = {{{type~}}}
+                            
+

+
+ + {{#if @root.github_source}} + {{#if definition_line}} +
+ [src] +
+ {{/if}} + {{/if}} +
{{markdown comment}}
{{/each}} @@ -66,13 +85,25 @@

Values

{{#each record.values}} -

-
let {{name}}
-                    {{~#each args~}}
-                        {{~#if implicit}} ?{{name}}{{else}} {{name}}{{~/if~}}
-                    {{~/each}} : {{{type~}}}
-                
-

+
+
+

+
let {{name}}
+                            {{~#each args~}}
+                                {{~#if implicit}} ?{{name}}{{else}} {{name}}{{~/if~}}
+                            {{~/each}} : {{{type~}}}
+                        
+

+
+ + {{#if @root.github_source}} + {{#if definition_line}} +
+ [src] +
+ {{/if}} + {{/if}} +
{{markdown comment}}
{{/each}} diff --git a/doc/src/lib.rs b/doc/src/lib.rs index 80ca75a140..a95bcd4dc0 100644 --- a/doc/src/lib.rs +++ b/doc/src/lib.rs @@ -45,7 +45,9 @@ use pretty::{Arena, DocAllocator}; use gluon::{ base::{ filename_to_module, + fnv::FnvMap, metadata::Metadata, + source::Source, symbol::{Name, Symbol}, types::{ArcType, ArgType, Type, TypeExt}, }, @@ -60,6 +62,7 @@ pub type Result = ::std::result::Result; pub struct Module { pub name: String, pub comment: String, + pub github_source: Option, pub record: Record, } @@ -82,6 +85,7 @@ pub struct Field { #[serde(rename = "type")] pub typ: String, pub comment: String, + pub definition_line: Option, } struct SymbolLinkRenderer { @@ -157,29 +161,62 @@ fn hidden(meta: &Metadata, field: &str) -> bool { }) } -pub fn record(current_module: &str, typ: &ArcType, meta: &Metadata) -> Record { +pub fn record( + current_module: &str, + typ: &ArcType, + symbols: &FnvMap<&Symbol, completion::SpCompletionSymbol>, + source: &Source, + meta: &Metadata, +) -> Record { + let line_number = |meta: &Metadata| -> Option { + meta.definition + .as_ref() + .and_then(|definition| symbols.get(definition)) + .and_then(|completion_symbol| { + source.line_number_at_byte(completion_symbol.span.start()) + }) + .map(|l| l.number().0) + }; + Record { types: typ .type_field_iter() .filter(|field| !hidden(meta, field.name.as_ref())) - .map(|field| Field { - name: field.name.definition_name().to_string(), - args: field - .typ - .params() - .iter() - .map(|gen| Argument { - implicit: false, - name: gen.id.to_string(), - }) - .collect(), - typ: print_type(current_module, &field.typ.unresolved_type().remove_forall()), - comment: meta - .module - .get(AsRef::::as_ref(&field.name)) - .and_then(|meta| meta.comment.as_ref().map(|s| &s.content[..])) - .unwrap_or("") - .to_string(), + .map(|field| { + let comment; + let definition_line; + + match meta.module.get(AsRef::::as_ref(&field.name)) { + Some(meta) => { + comment = meta + .comment + .as_ref() + .map(|s| &s.content[..]) + .unwrap_or("") + .to_string(); + definition_line = None; // FIXME line_number(meta); + } + None => { + comment = "".to_string(); + definition_line = None; + } + } + + Field { + name: field.name.definition_name().to_string(), + args: field + .typ + .params() + .iter() + .map(|gen| Argument { + implicit: false, + name: gen.id.to_string(), + }) + .collect(), + typ: print_type(current_module, &field.typ.unresolved_type().remove_forall()), + comment, + definition_line, + } }) .collect(), @@ -187,26 +224,41 @@ pub fn record(current_module: &str, typ: &ArcType, meta: &Metadata) -> Record { .row_iter() .filter(|field| !hidden(meta, field.name.as_ref())) .map(|field| { - let meta_opt = meta.module.get(AsRef::::as_ref(&field.name)); + let args; + let comment; + let definition_line; + + match meta.module.get(AsRef::::as_ref(&field.name)) { + Some(meta) => { + args = meta + .args + .iter() + .map(|arg| Argument { + implicit: arg.arg_type == ArgType::Implicit, + name: arg.name.definition_name().to_string(), + }) + .collect(); + comment = meta + .comment + .as_ref() + .map(|s| &s.content[..]) + .unwrap_or("") + .to_string(); + definition_line = line_number(meta); + } + _ => { + args = Vec::new(); + comment = "".to_string(); + definition_line = None; + } + } + Field { name: field.name.definition_name().to_string(), - args: meta_opt - .map(|meta| { - meta.args - .iter() - .map(|arg| Argument { - implicit: arg.arg_type == ArgType::Implicit, - name: arg.name.definition_name().to_string(), - }) - .collect() - }) - .unwrap_or_default(), + args, typ: print_type(current_module, &field.typ), - - comment: meta_opt - .and_then(|meta| meta.comment.as_ref().map(|s| &s.content[..])) - .unwrap_or("") - .to_string(), + comment, + definition_line, } }) .collect(), @@ -216,6 +268,7 @@ pub fn record(current_module: &str, typ: &ArcType, meta: &Metadata) -> Record { #[derive(Serialize, Debug)] pub struct TemplateModule<'a> { pub name: &'a str, + pub github_source: Option<&'a str>, pub comment: &'a str, pub record: &'a Record, pub sub_modules: Vec<&'a Module>, @@ -255,6 +308,21 @@ fn handlebars() -> Result { reg.register_template_string(MODULE_TEMPLATE, include_str!("doc/module.html"))?; + reg.register_helper( + "symbol_to_path", + Box::new( + move |h: &Helper, + _: &Handlebars, + _context: &Context, + _rc: &mut RenderContext, + out: &mut Output| { + let param = String::deserialize(h.param(0).unwrap().value())?; + out.write(¶m.replace(".", "/"))?; + Ok(()) + }, + ), + ); + fn module_link_helper( h: &Helper, _: &Handlebars, @@ -417,53 +485,70 @@ where Ok(()) } -pub fn generate_for_path(thread: &Thread, path: &P, out_path: &Q) -> Result<()> -where - P: ?Sized + AsRef, - Q: ?Sized + AsRef, -{ - generate_for_path_(thread, path.as_ref(), out_path.as_ref()) +struct DocCollector<'a> { + directories: BTreeMap>, + modules: BTreeSet, + content: String, + parent: Option<&'a Path>, + out_path: &'a Path, + thread: &'a Thread, } -pub fn generate_for_path_(thread: &Thread, path: &Path, out_path: &Path) -> Result<()> { - let mut directories = BTreeMap::<_, BTreeMap<_, Module>>::new(); - let mut modules = BTreeSet::new(); - let mut content = String::new(); - - let parent = path.parent(); - - for entry in walkdir::WalkDir::new(path) { - let entry = entry?; +impl DocCollector<'_> { + fn try_add_path(&mut self, entry: walkdir::DirEntry) -> Result<()> { if !entry.file_type().is_file() || entry.path().extension().and_then(|ext| ext.to_str()) != Some("glu") { - continue; + return Ok(()); } - let entry_path = entry.path(); - debug!("Indexing module: {}", entry_path.display()); - let mut input = File::open(&*entry_path).with_context(|err| { - format!( - "Unable to open gluon file `{}`: {}", - entry.path().display(), - err - ) + let module = self.module_for(entry.path())?; + + let DocCollector { + directories, + modules, + .. + } = self; + + modules.insert(module.name.clone()); + let name = Name::new(&module.name); + directories + .entry(name.module().as_str().to_owned()) + .or_default() + .insert(name.name().as_str().to_owned(), module); + + Ok(()) + } + + fn module_for(&mut self, path: &Path) -> Result { + let DocCollector { + content, + parent, + out_path, + thread, + .. + } = self; + + debug!("Indexing module: {}", path.display()); + + let mut input = File::open(&*path).with_context(|err| { + format!("Unable to open gluon file `{}`: {}", path.display(), err) })?; + content.clear(); - input.read_to_string(&mut content)?; + input.read_to_string(content)?; let module_path = parent - .and_then(|parent| entry_path.strip_prefix(parent).ok()) - .unwrap_or(entry_path); + .and_then(|parent| path.strip_prefix(parent).ok()) + .unwrap_or(path); let name = filename_to_module( module_path .to_str() .ok_or_else(|| failure::err_msg("Non-UTF-8 filename"))?, ); - let (expr, typ) = Compiler::new() - .full_metadata(true) - .typecheck_str(thread, &name, &content, None)?; + let mut compiler = Compiler::new().full_metadata(true); + let (expr, typ) = compiler.typecheck_str(thread, &name, &content, None)?; let (meta, _) = metadata(&*thread.get_env(), &expr); create_dir_all(out_path.join(module_path.parent().unwrap_or(Path::new(""))))?; @@ -477,20 +562,60 @@ pub fn generate_for_path_(thread: &Thread, path: &Path, out_path: &Path) -> Resu .format("\n") .to_string(); - let module = Module { - record: record(&name, &typ, &meta), + let source = compiler + .get_filemap(&name) + .expect("SourceMap not inserted by compilation"); + + let symbols = completion::all_symbols(source.span(), &expr) + .into_iter() + .map(|s| (s.value.name, s)) + .collect::>(); + + Ok(Module { + record: record(&name, &typ, &symbols, &*source, &meta), name, + github_source: meta + .get_attribute("github") + .map(|s| s.trim_matches('"').to_string()), comment, - }; + }) + } +} - modules.insert(module.name.clone()); - let name = Name::new(&module.name); - directories - .entry(name.module().as_str().to_owned()) - .or_default() - .insert(name.name().as_str().to_owned(), module); +pub fn generate_for_path(thread: &Thread, path: &P, out_path: &Q) -> Result<()> +where + P: ?Sized + AsRef, + Q: ?Sized + AsRef, +{ + generate_for_path_(thread, path.as_ref(), out_path.as_ref()) +} + +pub fn generate_for_path_(thread: &Thread, path: &Path, out_path: &Path) -> Result<()> { + let mut collector = DocCollector { + directories: BTreeMap::new(), + modules: BTreeSet::new(), + parent: path.parent(), + content: String::new(), + thread, + out_path, + }; + + let mut github_source = None; + { + let root_glu = path.with_extension("glu"); + if root_glu.exists() { + let module = collector.module_for(&root_glu)?; + github_source = module.github_source; + } + } + for entry in walkdir::WalkDir::new(path) { + collector.try_add_path(entry?)?; } + let DocCollector { + mut directories, .. + } = collector; + let directory_modules = directories.keys().cloned().collect::>(); for module_name in directory_modules { let name = Name::new(&module_name); @@ -504,6 +629,7 @@ pub fn generate_for_path_(thread: &Thread, path: &Path, out_path: &Path) -> Resu Module { name: module_name.into(), comment: "".into(), + github_source: None, record: Record::default(), } }); @@ -531,6 +657,7 @@ pub fn generate_for_path_(thread: &Thread, path: &Path, out_path: &Path) -> Resu &mut doc_file, &TemplateModule { name: &module.name, + github_source: github_source.as_ref().map(|s| &s[..]), comment: &module.comment, record: &module.record, sub_modules: directories diff --git a/std.glu b/std.glu new file mode 100644 index 0000000000..d7dde15ca4 --- /dev/null +++ b/std.glu @@ -0,0 +1,5 @@ +//! The gluon standard library + +#[github("https://github.com/gluon-lang/gluon")] +let std = {} +std