-
Notifications
You must be signed in to change notification settings - Fork 3
Support doc query #85
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
base: main
Are you sure you want to change the base?
Changes from all commits
2dbc6fa
66ec14a
1ab9842
ffbb59d
1140c34
88294f3
34101d2
a576847
11ed5cd
ec51ab4
93395a1
5e8a31e
117cf32
f2f2f6f
6acb25c
e6db24d
593f707
d34cb37
c07669c
a3ede72
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -13,8 +13,9 @@ jo compile --python|--ruby|--js [--sast <dir>] <file.jo>... \ | |||||||||||||||
| [--lib <dir>]... [--link-lib <dir>]... [--link <src>=<tgt>]... -o <output> | ||||||||||||||||
|
|
||||||||||||||||
| # Generate documentation from source files (experimental) | ||||||||||||||||
| jo compile --doc [--out <dir>] [--title <name>] [--readme <file>] \ | ||||||||||||||||
| [--include-private] [--include-source] <file.jo>... | ||||||||||||||||
| jo compile --doc [--format html|json] [--query <selectors>] [--out <dir>] \ | ||||||||||||||||
| [--title <name>] [--readme <file>] [--include-private] \ | ||||||||||||||||
| [--include-source] [<file.jo>...] | ||||||||||||||||
| ``` | ||||||||||||||||
|
|
||||||||||||||||
| Without a backend flag, the compiler type-checks only. With a backend flag, it produces an executable or script. `--sast <dir>` is optional in both cases — if present, `.sast` files are written to `<dir>` alongside the primary output. | ||||||||||||||||
|
|
@@ -44,12 +45,24 @@ Experimental. | |||||||||||||||
| | Flag | Description | | ||||||||||||||||
| |------|-------------| | ||||||||||||||||
| | `--doc` | Generate documentation instead of normal compile output | | ||||||||||||||||
| | `--format html|json` | Documentation output format. Default: `html` | | ||||||||||||||||
| | `--query <selectors>` | Comma-separated JSON selectors, such as `MyAPI` or `file:src/API.jo`; implies `--format json` | | ||||||||||||||||
| | `--out <dir>` | Documentation output directory | | ||||||||||||||||
| | `--title <name>` | Documentation title | | ||||||||||||||||
| | `--readme <file>` | Markdown file to use as the generated documentation home page | | ||||||||||||||||
| | `--include-private` | Include private symbols | | ||||||||||||||||
| | `--include-source` | Embed source code in output | | ||||||||||||||||
|
|
||||||||||||||||
| `--format json` writes a JSON array to stdout. `--query` implies | ||||||||||||||||
| `--format json`. JSON output does not use `--out`. | ||||||||||||||||
| Without `--query`, JSON output contains the public API surface from the | ||||||||||||||||
| positional source files. With `--query`, selectors are comma-separated and may | ||||||||||||||||
| name symbols or source files with `file:<path>`. | ||||||||||||||||
| Symbol selectors are resolved from the language default scope; for example `~` | ||||||||||||||||
| is just an ordinary symbol query. A query may omit positional source files; in | ||||||||||||||||
| that case it searches the loaded SAST libraries, including stdlib unless | ||||||||||||||||
| `--no-stdlib` is set and any `--lib` paths. | ||||||||||||||||
|
|
||||||||||||||||
|
Comment on lines
+62
to
+65
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bit of a ranaway sentence. If I understood it correctly, here is how I would make it easier to read:
Suggested change
But correct me if I misunderstood it. |
||||||||||||||||
| ### App compilation | ||||||||||||||||
|
|
||||||||||||||||
| | Flag | Description | | ||||||||||||||||
|
|
@@ -104,6 +117,24 @@ jo compile --doc lib/Core.jo lib/List.jo \ | |||||||||||||||
| --title "Jo Standard Library" | ||||||||||||||||
| ``` | ||||||||||||||||
|
|
||||||||||||||||
| Emit machine-readable docs for a source file: | ||||||||||||||||
|
|
||||||||||||||||
| ```sh | ||||||||||||||||
| jo compile --doc --format json src/API.jo | ||||||||||||||||
| ``` | ||||||||||||||||
|
|
||||||||||||||||
| Emit machine-readable docs for selected symbols: | ||||||||||||||||
|
|
||||||||||||||||
| ```sh | ||||||||||||||||
| jo compile --doc --query 'MyAPI,jo.py' src/API.jo | ||||||||||||||||
| ``` | ||||||||||||||||
|
|
||||||||||||||||
| Query stdlib docs from SAST files only: | ||||||||||||||||
|
|
||||||||||||||||
| ```sh | ||||||||||||||||
| jo compile --doc --query jo.List | ||||||||||||||||
| ``` | ||||||||||||||||
|
|
||||||||||||||||
| ## Notes | ||||||||||||||||
|
|
||||||||||||||||
| `jo compile` is a low-level escape hatch for scripts, CI pipelines, or experiments that don't fit the standard project layout. For normal project workflows, prefer the higher-level commands such as `jo build`, `jo run <module>`, and [`jo doc`](doc.md). | ||||||||||||||||
|
|
||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -94,8 +94,26 @@ above plus these documentation-specific options. | |
|
|
||
| | Option | Form | Description | | ||
| |--------|------|-------------| | ||
| | `--format <html|json>` | single value | Documentation output format. Default: `html`. | | ||
| | `--query <selectors>` | comma list | Select symbols or source files for JSON documentation output. Implies `--format json`. | | ||
| | `--out <dir>` | single value | Documentation output directory. Default: `docs`. | | ||
| | `--title <name>` | single value | Documentation title. Default: `API Documentation`. | | ||
| | `--readme <file>` | single value | Markdown file to use as the generated documentation home page. | | ||
| | `--include-private` | flag | Include private symbols. | | ||
| | `--include-source` | flag | Embed source code in the generated documentation. | | ||
|
|
||
| `--format json` writes a bare JSON array to stdout and rejects `--out`. | ||
| `--query` implies `--format json`. | ||
| Without `--query`, it emits the public API surface from the positional source | ||
| files. `--query` accepts comma-separated selectors: | ||
|
|
||
| | Selector | Meaning | | ||
| |----------|---------| | ||
| | `<symbol>` | Symbol resolved from the language default scope. Aggregate symbols are emitted structurally with their members. | | ||
| | `file:<path>` | Queryable symbols whose source file matches the path. | | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you have a use case in mind for the |
||
|
|
||
| When `--query` is present, positional source files are optional. With no source | ||
| files, the query searches the loaded SAST libraries: stdlib by default, runtime | ||
| API libraries selected by `--use-runtime-api`, and any `--lib` paths. | ||
|
|
||
| Kind-qualified selectors such as `def:<symbol>` are reserved for future use. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| package doc | ||
|
|
||
| import sast.* | ||
| import sast.Trees.FileUnit | ||
|
|
||
| import typing.Typer | ||
| import reporting.Reporter | ||
|
|
@@ -17,30 +18,41 @@ object Compiler: | |
| val readme: Config.StringSetting = Config.StringSetting("--readme", "", "markdown file to use as home page") | ||
| val includePrivate: Config.BooleanSetting = Config.BooleanSetting("--include-private", false, "include private symbols") | ||
| val includeSource: Config.BooleanSetting = Config.BooleanSetting("--include-source", false, "embed source code") | ||
| val format: Config.StringSetting = DocFormatSetting("--format", "html", "documentation output format: html or json") | ||
| val query: Config.StringSetting = Config.StringSetting("--query", "", "comma-separated documentation selectors for JSON output") | ||
|
|
||
| val docOptions: List[cli.OptionParser.Setting[?]] = | ||
| outputDir :: title :: readme :: includePrivate :: includeSource :: Config.commonOptions | ||
| outputDir :: title :: readme :: includePrivate :: includeSource :: format :: query :: Config.commonOptions | ||
|
|
||
| class DocFormatSetting(flag: String, default: String, desc: String) | ||
| extends Config.StringSetting(flag, default, desc): | ||
| override def validate()(using cf: Config, rp: Reporter): Unit = | ||
| val fmt = value | ||
| if fmt != "html" && fmt != "json" then | ||
| rp.error(s"Option $flag must be one of: html, json") | ||
|
|
||
| def main(args: Array[String]): Unit = | ||
| given Reporter = Reporter.createReporter() | ||
|
|
||
| val (config, sources) = cli.OptionParser.parseConfig(args, docOptions) | ||
|
|
||
| if sources.isEmpty then | ||
| given Config = config | ||
|
|
||
| if sources.isEmpty && query.value.trim.isEmpty then | ||
| println("Usage: jo doc <sources...> [options]") | ||
| println() | ||
| println("Options:") | ||
| println(" --out <dir> Output directory (default: docs)") | ||
| println(" --title <name> Project title for documentation") | ||
| println(" --format <html|json> Output format (default: html)") | ||
| println(" --query <selectors> JSON selectors, e.g. jo.py,file:src/API.jo") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think using a symbolic selector that resembles a file with a classic extension together with a file selector is confusing, especially for people that are not aware that |
||
| println(" --include-private Include private symbols") | ||
| println(" --include-source Embed source code in output") | ||
| println() | ||
| println("Examples:") | ||
| println(" jo doc lib/Core.jo lib/List.jo --out site/api") | ||
| println(" jo doc src/main.jo --out docs --title MyProject") | ||
| return | ||
|
|
||
| given Config = config | ||
| println(" jo doc --query jo.List") | ||
| System.exit(1) | ||
|
|
||
| Reporter.monitor(): | ||
| compile(sources) | ||
|
|
@@ -49,16 +61,55 @@ object Compiler: | |
| def compile(sources: List[String])(using rp: Reporter, config: Config): Unit = | ||
| val rootNameTable = new NameTable | ||
| given lazyDefn: Definitions.Lazy = Definitions.Lazy(rootNameTable) | ||
| val queryText = query.value.trim | ||
| val jsonOutput = format.value == "json" || queryText.nonEmpty | ||
|
|
||
| // Parse and type check | ||
| val (units, _) = sources |> Typer.parseStep |> Typer.typeStep | ||
| def writeJson(units: List[FileUnit], filter: DocQuery.Filter)(using Definitions): Unit = | ||
| val out = new PrintWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8)) | ||
| DocQuery.emitJson(units, filter, includePrivate.value, out) | ||
| out.flush() | ||
|
|
||
| if rp.hasErrors then | ||
| println("Errors occurred during type checking. Documentation not generated.") | ||
| if jsonOutput && sources.isEmpty && Config.libPaths.value.isEmpty then | ||
| if outputDir.value != outputDir.default then | ||
| Reporter.error("--out is only supported with --format html") | ||
| return | ||
|
|
||
| DocQuery.reportNoMatches(queryText) | ||
| return | ||
|
|
||
| given Definitions = lazyDefn.value | ||
| // Parse and type check | ||
| val (units, delayedUnits) = sources |> Typer.parseStep |> Typer.typeStep | ||
|
|
||
| if rp.hasErrors then return | ||
|
|
||
| given defn: Definitions = lazyDefn.value | ||
|
|
||
| if jsonOutput then | ||
| if outputDir.value != outputDir.default then | ||
| Reporter.error("--out is only supported with --format html") | ||
| return | ||
|
|
||
| val filter = DocQuery.parse(queryText, defn.rootNameTable) | ||
| if rp.hasErrors then return | ||
|
|
||
| val libraryUnits = | ||
| if queryText.isEmpty then | ||
| Nil | ||
| else | ||
| delayedUnits.forceIf: unit => | ||
| filter.symbols.exists: querySymbol => | ||
| unit.owner.containedIn(querySymbol) || querySymbol.containedIn(unit.owner) | ||
|
|
||
| delayedUnits.force() | ||
|
|
||
| val filteredUnits = DocQuery.filterUnits(units, libraryUnits, filter) | ||
| if rp.hasErrors then return | ||
| writeJson(filteredUnits, filter) | ||
|
|
||
| else | ||
| generateHtmlDoc(units) | ||
|
|
||
| def generateHtmlDoc(units: List[FileUnit])(using Config, Definitions): Unit = | ||
| val outputPath = Paths.get(outputDir.value) | ||
| val includePrivateVal = includePrivate.value | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is
~symbol? It looks like some kind of special symbol. Is it actually a Jo operator~? If so, it is a confusing example for the documentation.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's a little confusing, I'll remove it.
~is defined in standard library: