rune/cli/
ace.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::io::Write;
use std::path::PathBuf;

use crate::doc::Artifacts;

use anyhow::{Context, Result};
use clap::Parser;

use crate::alloc::prelude::*;
use crate::alloc::Vec;
use crate::cli::naming::Naming;
use crate::cli::{AssetKind, CommandBase, Config, Entry, EntryPoint, ExitCode, Io, SharedFlags};
use crate::compile::FileSourceLoader;
use crate::{Diagnostics, Options, Source, Sources};

#[derive(Parser, Debug)]
pub(super) struct Flags {
    /// Output directory to write ace extensions to.
    #[arg(long)]
    output: Option<PathBuf>,
    /// Generate .await and ? extension for functions.
    #[arg(long)]
    extensions: bool,
    /// Do not include `rune-mode.js`.
    #[arg(long)]
    no_mode: bool,
    /// Exit with a non-zero exit-code even for warnings
    #[arg(long)]
    warnings_are_errors: bool,
}

impl CommandBase for Flags {
    #[inline]
    fn is_workspace(&self, _: AssetKind) -> bool {
        true
    }

    #[inline]
    fn describe(&self) -> &str {
        "Documenting"
    }
}

pub(super) fn run<'p, I>(
    io: &mut Io<'_>,
    entry: &mut Entry<'_>,
    c: &Config,
    flags: &Flags,
    shared: &SharedFlags,
    options: &Options,
    entries: I,
) -> Result<ExitCode>
where
    I: IntoIterator<Item = EntryPoint<'p>>,
{
    let root = match &flags.output {
        Some(root) => root.clone(),
        None => match &c.manifest_root {
            Some(path) => path.join("target").join("rune-ace"),
            None => match std::env::var_os("CARGO_TARGET_DIR") {
                Some(target) => {
                    let mut target = PathBuf::from(target);
                    target.push("rune-ace");
                    target
                }
                None => {
                    let mut target = PathBuf::new();
                    target.push("target");
                    target.push("rune-ace");
                    target
                }
            },
        },
    };

    writeln!(io.stdout, "Building ace autocompletion: {}", root.display())?;

    let context = shared.context(entry, c, None)?;

    let mut visitors = Vec::new();

    let mut naming = Naming::default();

    for e in entries {
        let item = naming.item(&e)?;

        let mut visitor = crate::doc::Visitor::new(&item)?;
        let mut sources = Sources::new();

        let source = match Source::from_path(e.path()) {
            Ok(source) => source,
            Err(error) => return Err(error).context(e.path().display().try_to_string()?),
        };

        sources.insert(source)?;

        let mut diagnostics = if shared.warnings || flags.warnings_are_errors {
            Diagnostics::new()
        } else {
            Diagnostics::without_warnings()
        };

        let mut source_loader = FileSourceLoader::new();

        let _ = crate::prepare(&mut sources)
            .with_context(&context)
            .with_diagnostics(&mut diagnostics)
            .with_options(options)
            .with_visitor(&mut visitor)?
            .with_source_loader(&mut source_loader)
            .build();

        diagnostics.emit(&mut io.stdout.lock(), &sources)?;

        if diagnostics.has_error() || flags.warnings_are_errors && diagnostics.has_warning() {
            return Ok(ExitCode::Failure);
        }

        visitors.try_push(visitor)?;
    }

    let mut artifacts = Artifacts::new();
    crate::ace::build_autocomplete(&mut artifacts, &context, &visitors, flags.extensions)?;

    if !flags.no_mode {
        crate::ace::theme(&mut artifacts)?;
    }

    for asset in artifacts.assets() {
        asset.build(&root)?;
    }

    Ok(ExitCode::Success)
}