mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
Substitute usage information into the documentation
This will simplify keeping documentation in sync with the code.
This commit is contained in:
parent
7f046a8e27
commit
09558f9329
21 changed files with 1177 additions and 55 deletions
20
.github/workflows/publish-docs.yml
vendored
20
.github/workflows/publish-docs.yml
vendored
|
@ -17,6 +17,26 @@ jobs:
|
|||
with:
|
||||
mdbook-version: 'latest'
|
||||
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
|
||||
- name: Create usage-docs plugin
|
||||
run: cargo build -p taskchampion-cli --features usage-docs --bin usage-docs
|
||||
|
||||
- run: mdbook build docs
|
||||
|
||||
- name: Deploy
|
||||
|
|
20
.github/workflows/rust-tests.yml
vendored
20
.github/workflows/rust-tests.yml
vendored
|
@ -75,5 +75,25 @@ jobs:
|
|||
with:
|
||||
mdbook-version: 'latest'
|
||||
|
||||
- name: Cache cargo registry
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo/registry
|
||||
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- name: Cache cargo build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
|
||||
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
|
||||
- name: Create usage-docs plugin
|
||||
run: cargo build -p taskchampion-cli --features usage-docs --bin usage-docs
|
||||
|
||||
- run: mdbook test docs
|
||||
- run: mdbook build docs
|
||||
|
|
844
Cargo.lock
generated
844
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -33,3 +33,8 @@ There are three crates here:
|
|||
* [taskchampion-cli](./cli) - the command-line binary
|
||||
* [taskchampion-sync-server](./sync-server) - the server against which `task sync` operates
|
||||
|
||||
## Documentation Generation
|
||||
|
||||
The `mdbook` configuration contains a "preprocessor" implemented in the `taskchampion-cli` crate in order to reflect CLI usage information into the generated book.
|
||||
Tihs preprocessor is not built by default.
|
||||
To (re)build it, run `cargo build -p taskchampion-cli --features usage-docs --bin usage-docs`.
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
1. Run `git tag vX.Y.Z`
|
||||
1. Run `git push upstream`
|
||||
1. Run `git push --tags upstream`
|
||||
1. Run `( cd docs; ./build.sh )`
|
||||
1. Run `( ./build-docs.sh )`
|
||||
1. Run `(cd taskchampion; cargo publish)` (note that the other crates do not get published)
|
||||
1. Navigate to the tag in the GitHub releases UI and create a release with general comments about the changes in the release
|
||||
1. Upload `./target/release/task` and `./target/release/task-sync-server` to the release
|
||||
|
|
31
build-docs.sh
Executable file
31
build-docs.sh
Executable file
|
@ -0,0 +1,31 @@
|
|||
#! /bin/bash
|
||||
|
||||
REMOTE=origin
|
||||
|
||||
set -e
|
||||
|
||||
if ! [ -f "docs/src/SUMMARY.md" ]; then
|
||||
echo "Run this from the root of the repo"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# build the latest version of the mdbook plugin
|
||||
cargo build -p taskchampion-cli --features usage-docs --bin usage-docs
|
||||
|
||||
# create a worktree of this repo, with the `gh-pages` branch checked out
|
||||
if ! [ -d ./docs/tmp ]; then
|
||||
git worktree add docs/tmp gh-pages
|
||||
fi
|
||||
|
||||
# update the wortree
|
||||
(cd docs/tmp && git pull $REMOTE gh-pages)
|
||||
|
||||
# remove all files in the worktree and regenerate the book there
|
||||
rm -rf docs/tmp/*
|
||||
mdbook build docs
|
||||
cp -rp docs/book/* docs/tmp
|
||||
|
||||
# add everything in the worktree, commit, and push
|
||||
(cd docs/tmp && git add -A)
|
||||
(cd docs/tmp && git commit -am "update docs")
|
||||
(cd docs/tmp && git push $REMOTE gh-pages:gh-pages)
|
|
@ -20,6 +20,10 @@ atty = "^0.2.14"
|
|||
toml = "^0.5.8"
|
||||
toml_edit = "^0.2.0"
|
||||
|
||||
# only needed for usage-docs
|
||||
mdbook = { version = "0.4", optional = true }
|
||||
serde_json = { version = "*", optional = true }
|
||||
|
||||
[dependencies.taskchampion]
|
||||
path = "../taskchampion"
|
||||
|
||||
|
@ -27,3 +31,14 @@ path = "../taskchampion"
|
|||
assert_cmd = "^1.0.3"
|
||||
predicates = "^1.0.7"
|
||||
tempfile = "3"
|
||||
|
||||
[features]
|
||||
usage-docs = [ "mdbook", "serde_json" ]
|
||||
|
||||
[[bin]]
|
||||
name = "ta"
|
||||
|
||||
[[bin]]
|
||||
# this is an mdbook plugin and only needed when running `mdbook`
|
||||
name = "usage-docs"
|
||||
required-features = [ "usage-docs" ]
|
||||
|
|
|
@ -115,7 +115,9 @@ impl Modification {
|
|||
summary: "Set description",
|
||||
description: "
|
||||
Set the task description. Multiple arguments are combined into a single
|
||||
space-separated description.",
|
||||
space-separated description. To avoid surprises from shell quoting, prefer
|
||||
to use a single quoted argument, for example `ta 19 modify \"return library
|
||||
books\"`",
|
||||
});
|
||||
u.modifications.push(usage::Modification {
|
||||
syntax: "+TAG",
|
||||
|
|
50
cli/src/bin/usage-docs.rs
Normal file
50
cli/src/bin/usage-docs.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
use mdbook::book::{Book, BookItem};
|
||||
use mdbook::errors::Error;
|
||||
use mdbook::preprocess::{CmdPreprocessor, PreprocessorContext};
|
||||
use std::io;
|
||||
use std::process;
|
||||
use taskchampion_cli::Usage;
|
||||
|
||||
/// This is a simple mdbook preprocessor designed to substitute information from the usage
|
||||
/// into the documentation.
|
||||
fn main() -> anyhow::Result<()> {
|
||||
// cheap way to detect the "supports" arg
|
||||
if std::env::args().len() > 1 {
|
||||
// sure, whatever, we support it all
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?;
|
||||
|
||||
if ctx.mdbook_version != mdbook::MDBOOK_VERSION {
|
||||
eprintln!(
|
||||
"Warning: This mdbook preprocessor was built against version {} of mdbook, \
|
||||
but we're being called from version {}",
|
||||
mdbook::MDBOOK_VERSION,
|
||||
ctx.mdbook_version
|
||||
);
|
||||
}
|
||||
|
||||
let processed_book = process(&ctx, book)?;
|
||||
serde_json::to_writer(io::stdout(), &processed_book)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process(_ctx: &PreprocessorContext, mut book: Book) -> Result<Book, Error> {
|
||||
let usage = Usage::new();
|
||||
|
||||
book.for_each_mut(|sect| {
|
||||
if let BookItem::Chapter(ref mut chapter) = sect {
|
||||
let new_content = usage.substitute_docs(&chapter.content).unwrap();
|
||||
if new_content != chapter.content {
|
||||
eprintln!(
|
||||
"Substituting usage in {:?}",
|
||||
chapter.source_path.as_ref().unwrap()
|
||||
);
|
||||
}
|
||||
chapter.content = new_content;
|
||||
}
|
||||
});
|
||||
Ok(book)
|
||||
}
|
|
@ -47,6 +47,9 @@ mod usage;
|
|||
pub(crate) use errors::Error;
|
||||
use settings::Settings;
|
||||
|
||||
// used by the `generate` command
|
||||
pub use usage::Usage;
|
||||
|
||||
/// The main entry point for the command-line interface. This builds an Invocation
|
||||
/// from the particulars of the operating-system interface, and then executes it.
|
||||
pub fn main() -> Result<(), Error> {
|
||||
|
|
|
@ -7,5 +7,5 @@ mod report;
|
|||
mod settings;
|
||||
mod util;
|
||||
|
||||
pub(crate) use report::{Column, Property, Report, Sort, SortBy};
|
||||
pub(crate) use report::{get_usage, Column, Property, Report, Sort, SortBy};
|
||||
pub(crate) use settings::Settings;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use crate::argparse::{Condition, Filter};
|
||||
use crate::settings::util::table_with_keys;
|
||||
use crate::usage::{self, Usage};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
|
@ -30,6 +31,7 @@ pub(crate) struct Column {
|
|||
/// Task property to display in a report
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(crate) enum Property {
|
||||
// NOTE: when adding a property here, add it to get_usage, below, as well.
|
||||
/// The task's ID, either working-set index or Uuid if not in the working set
|
||||
Id,
|
||||
|
||||
|
@ -59,6 +61,7 @@ pub(crate) struct Sort {
|
|||
/// Task property to sort by
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(crate) enum SortBy {
|
||||
// NOTE: when adding a property here, add it to get_usage, below, as well.
|
||||
/// The task's ID, either working-set index or a UUID prefix; working
|
||||
/// set tasks sort before others.
|
||||
Id,
|
||||
|
@ -212,6 +215,34 @@ impl TryFrom<&toml::Value> for SortBy {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_usage(u: &mut Usage) {
|
||||
u.report_properties.push(usage::ReportProperty {
|
||||
name: "id",
|
||||
as_sort_by: Some("Sort by the task's shorthand ID"),
|
||||
as_column: Some("The task's shorthand ID"),
|
||||
});
|
||||
u.report_properties.push(usage::ReportProperty {
|
||||
name: "uuid",
|
||||
as_sort_by: Some("Sort by the task's full UUID"),
|
||||
as_column: Some("The task's full UUID"),
|
||||
});
|
||||
u.report_properties.push(usage::ReportProperty {
|
||||
name: "active",
|
||||
as_sort_by: None,
|
||||
as_column: Some("`*` if the task is active (started)"),
|
||||
});
|
||||
u.report_properties.push(usage::ReportProperty {
|
||||
name: "description",
|
||||
as_sort_by: Some("Sort by the task's description"),
|
||||
as_column: Some("The task's description"),
|
||||
});
|
||||
u.report_properties.push(usage::ReportProperty {
|
||||
name: "tags",
|
||||
as_sort_by: None,
|
||||
as_column: Some("The task's tags"),
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
136
cli/src/usage.rs
136
cli/src/usage.rs
|
@ -2,26 +2,31 @@
|
|||
//! a way that puts the source of that documentation near its implementation.
|
||||
|
||||
use crate::argparse;
|
||||
use std::io::{Result, Write};
|
||||
use crate::settings;
|
||||
use anyhow::Result;
|
||||
use std::io::Write;
|
||||
|
||||
#[cfg(feature = "usage-docs")]
|
||||
use std::fmt::Write as FmtWrite;
|
||||
|
||||
/// A top-level structure containing usage/help information for the entire CLI.
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct Usage {
|
||||
pub struct Usage {
|
||||
pub(crate) subcommands: Vec<Subcommand>,
|
||||
pub(crate) filters: Vec<Filter>,
|
||||
pub(crate) modifications: Vec<Modification>,
|
||||
pub(crate) report_properties: Vec<ReportProperty>,
|
||||
}
|
||||
|
||||
impl Usage {
|
||||
/// Get a new, completely-filled-out usage object
|
||||
pub(crate) fn new() -> Self {
|
||||
pub fn new() -> Self {
|
||||
let mut rv = Self {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
argparse::get_usage(&mut rv);
|
||||
|
||||
// TODO: sort subcommands
|
||||
settings::get_usage(&mut rv);
|
||||
|
||||
rv
|
||||
}
|
||||
|
@ -77,6 +82,62 @@ impl Usage {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "usage-docs")]
|
||||
/// Substitute strings matching
|
||||
///
|
||||
/// ```text
|
||||
/// <!-- INSERT GENERATED DOCUMENTATION - $type -->
|
||||
/// ```
|
||||
///
|
||||
/// With the appropriate documentation.
|
||||
pub fn substitute_docs(&self, content: &str) -> Result<String> {
|
||||
// this is not efficient, but it doesn't need to be
|
||||
let mut lines = content.lines();
|
||||
let mut w = String::new();
|
||||
|
||||
const DOC_HEADER_PREFIX: &str = "<!-- INSERT GENERATED DOCUMENTATION - ";
|
||||
const DOC_HEADER_SUFFIX: &str = " -->";
|
||||
|
||||
for line in lines {
|
||||
if line.starts_with(DOC_HEADER_PREFIX) && line.ends_with(DOC_HEADER_SUFFIX) {
|
||||
let doc_type = &line[DOC_HEADER_PREFIX.len()..line.len() - DOC_HEADER_SUFFIX.len()];
|
||||
|
||||
match doc_type {
|
||||
"subcommands" => {
|
||||
for subcommand in self.subcommands.iter() {
|
||||
subcommand.write_markdown(&mut w)?;
|
||||
}
|
||||
}
|
||||
"filters" => {
|
||||
for filter in self.filters.iter() {
|
||||
filter.write_markdown(&mut w)?;
|
||||
}
|
||||
}
|
||||
"modifications" => {
|
||||
for modification in self.modifications.iter() {
|
||||
modification.write_markdown(&mut w)?;
|
||||
}
|
||||
}
|
||||
"report-columns" => {
|
||||
for prop in self.report_properties.iter() {
|
||||
prop.write_column_markdown(&mut w)?;
|
||||
}
|
||||
}
|
||||
"report-sort-by" => {
|
||||
for prop in self.report_properties.iter() {
|
||||
prop.write_sort_by_markdown(&mut w)?;
|
||||
}
|
||||
}
|
||||
_ => anyhow::bail!("Unkonwn doc type {}", doc_type),
|
||||
}
|
||||
} else {
|
||||
writeln!(w, "{}", line)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(w)
|
||||
}
|
||||
}
|
||||
|
||||
/// wrap an indented string
|
||||
|
@ -122,6 +183,15 @@ impl Subcommand {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "usage-docs")]
|
||||
fn write_markdown<W: FmtWrite>(&self, mut w: W) -> Result<()> {
|
||||
writeln!(w, "### `ta {}` - {}", self.name, self.summary)?;
|
||||
writeln!(w, "```shell\nta {}\n```", self.syntax)?;
|
||||
writeln!(w, "{}", indented(self.description, ""))?;
|
||||
writeln!(w)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Usage documentation for a filter argument
|
||||
|
@ -152,6 +222,15 @@ impl Filter {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "usage-docs")]
|
||||
fn write_markdown<W: FmtWrite>(&self, mut w: W) -> Result<()> {
|
||||
writeln!(w, "* `{}` - {}", self.syntax, self.summary)?;
|
||||
writeln!(w)?;
|
||||
writeln!(w, "{}", indented(self.description, " "))?;
|
||||
writeln!(w)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Usage documentation for a modification argument
|
||||
|
@ -182,4 +261,51 @@ impl Modification {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "usage-docs")]
|
||||
fn write_markdown<W: FmtWrite>(&self, mut w: W) -> Result<()> {
|
||||
writeln!(w, "* `{}` - {}", self.syntax, self.summary)?;
|
||||
writeln!(w)?;
|
||||
writeln!(w, "{}", indented(self.description, " "))?;
|
||||
writeln!(w)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Usage documentation for a report property (which may be used for sorting, as a column, or
|
||||
/// both).
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct ReportProperty {
|
||||
/// Name of the property
|
||||
pub(crate) name: &'static str,
|
||||
|
||||
/// Usage description for sorting, if any
|
||||
pub(crate) as_sort_by: Option<&'static str>,
|
||||
|
||||
/// Usage description as a column, if any
|
||||
pub(crate) as_column: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl ReportProperty {
|
||||
#[cfg(feature = "usage-docs")]
|
||||
fn write_sort_by_markdown<W: FmtWrite>(&self, mut w: W) -> Result<()> {
|
||||
if let Some(as_sort_by) = self.as_sort_by {
|
||||
writeln!(w, "* `{}`", self.name)?;
|
||||
writeln!(w)?;
|
||||
writeln!(w, "{}", indented(as_sort_by, " "))?;
|
||||
writeln!(w)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "usage-docs")]
|
||||
fn write_column_markdown<W: FmtWrite>(&self, mut w: W) -> Result<()> {
|
||||
if let Some(as_column) = self.as_column {
|
||||
writeln!(w, "* `{}`", self.name)?;
|
||||
writeln!(w)?;
|
||||
writeln!(w, "{}", indented(as_column, " "))?;
|
||||
writeln!(w)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
This is an [mdbook](https://rust-lang.github.io/mdBook/index.html) book.
|
||||
Minor modifications can be made without installing the mdbook tool, as the content is simple Markdown.
|
||||
Changes are verified on pull requests.
|
||||
|
||||
To build the docs locally, you will need to build `usage-docs`:
|
||||
|
||||
```
|
||||
cargo build -p taskchampion-cli --feature usage-docs --bin usage-docs
|
||||
mdbook build docs/
|
||||
```
|
||||
|
|
|
@ -7,3 +7,6 @@ title = "TaskChampion"
|
|||
|
||||
[output.html]
|
||||
default-theme = "ayu"
|
||||
|
||||
[preprocessor.usage-docs]
|
||||
command = "target/debug/usage-docs"
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
#! /bin/bash
|
||||
|
||||
REMOTE=origin
|
||||
|
||||
set -e
|
||||
|
||||
if ! [ -f "./src/SUMMARY.md" ]; then
|
||||
echo "Run this from the docs/ dir"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [ -d ./tmp ]; then
|
||||
git worktree add tmp gh-pages
|
||||
fi
|
||||
|
||||
(cd tmp && git pull $REMOTE gh-pages)
|
||||
|
||||
rm -rf tmp/*
|
||||
mdbook build
|
||||
cp -rp book/* tmp
|
||||
(cd tmp && git add -A)
|
||||
(cd tmp && git commit -am "update docs")
|
||||
(cd tmp && git push $REMOTE gh-pages:gh-pages)
|
|
@ -3,9 +3,11 @@
|
|||
- [Welcome to TaskChampion](./welcome.md)
|
||||
* [Installation](./installation.md)
|
||||
* [Using the Task Command](./using-task-command.md)
|
||||
* [Configuration](./config-file.md)
|
||||
* [Reports](./reports.md)
|
||||
* [Tags](./tags.md)
|
||||
* [Filters](./filters.md)
|
||||
* [Modifications](./modifications.md)
|
||||
* [Configuration](./config-file.md)
|
||||
* [Environment](./environment.md)
|
||||
* [Synchronization](./task-sync.md)
|
||||
* [Running the Sync Server](./running-sync-server.md)
|
||||
|
|
9
docs/src/filters.md
Normal file
9
docs/src/filters.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Filters
|
||||
|
||||
Filters are used to select specific tasks for reports or to specify tasks to be modified.
|
||||
When more than one filter is given, only tasks which match all of the filters are selected.
|
||||
When no filter is given, the command implicitly selects all tasks.
|
||||
|
||||
Filters can have the following forms:
|
||||
|
||||
<!-- INSERT GENERATED DOCUMENTATION - filters -->
|
5
docs/src/modifications.md
Normal file
5
docs/src/modifications.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Modifications
|
||||
|
||||
Modifications can have the following forms:
|
||||
|
||||
<!-- INSERT GENERATED DOCUMENTATION - modifications-->
|
|
@ -49,9 +49,8 @@ columns = [
|
|||
]
|
||||
```
|
||||
|
||||
The filter is a list of filter arguments, just like those that can be used on the command line.
|
||||
See the `ta help` output for more details on this syntax.
|
||||
It will be merged with any filters provided on the command line, when the report is invoked.
|
||||
The `filter` property is a list of [filters](./filters.md).
|
||||
It will be merged with any filters provided on the command line when the report is invoked.
|
||||
|
||||
The sort order is defined by an array of tables containing a `sort_by` property and an optional `ascending` property.
|
||||
Tasks are compared by the first criterion, and if that is equal by the second, and so on.
|
||||
|
@ -70,11 +69,11 @@ sort = [
|
|||
|
||||
The available values of `sort_by` are
|
||||
|
||||
(TODO: generate automatically)
|
||||
<!-- INSERT GENERATED DOCUMENTATION - report-sort-by -->
|
||||
|
||||
Finally, the `columns` configuration specifies the list of columns to display.
|
||||
Each element has a `label` and a `property`, as shown in the example above.
|
||||
|
||||
The avaliable properties are:
|
||||
|
||||
(TODO: generate automatically)
|
||||
<!-- INSERT GENERATED DOCUMENTATION - report-columns -->
|
||||
|
|
|
@ -4,6 +4,13 @@ The main interface to your tasks is the `ta` command, which supports various sub
|
|||
Customizable [reports](./reports.md) are also available as subcommands, such as `next`.
|
||||
The command reads a [configuration file](./config-file.md) for its settings, including where to find the task database.
|
||||
And the `sync` subcommand [synchronizes tasks with a sync server](./task-sync.md).
|
||||
You can find a list of all subcommands, as well as the built-in reports, with `ta help`.
|
||||
|
||||
> NOTE: the `task` interface does not precisely match that of TaskWarrior.
|
||||
|
||||
## Subcommands
|
||||
|
||||
The sections below describe each subcommand of the `ta` command.
|
||||
The syntax of `[filter]` is defined in [filters](./filters.md), and that of `[modification]` in [modifications](./modifications.md).
|
||||
You can also find a summary of all subcommands, as well as filters, built-in reports, and so on, with `ta help`.
|
||||
|
||||
<!-- INSERT GENERATED DOCUMENTATION - subcommands -->
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue