summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/main.yml118
-rw-r--r--Cargo.lock164
-rw-r--r--Cargo.toml9
-rw-r--r--readme.md8
-rw-r--r--src/app/impl_self.rs60
-rw-r--r--src/app/impl_view.rs38
-rw-r--r--src/command.rs87
-rw-r--r--src/habit/bit.rs4
-rw-r--r--src/main.rs23
-rw-r--r--src/theme.rs20
-rw-r--r--src/utils.rs130
-rw-r--r--src/views.rs35
12 files changed, 568 insertions, 128 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..8786029
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,118 @@
+name: Rust
+
+on:
+ push:
+ tags:
+ - '*'
+
+jobs:
+ rustfmt:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v1
+ - run: rustup component add rustfmt
+ - run: cargo fmt -- --check
+
+ build-linux:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v1
+ # cache the build assets so they dont recompile every time.
+ - name: Cache Rust dependencies
+ uses: actions/cache@v1.0.1
+ with:
+ path: target
+ key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.OS }}-build-
+ - name: Install latest rust toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: beta
+ default: true
+ override: true
+ - name: Install system dependencies
+ run: |
+ sudo apt-get update \
+ && sudo apt-get install -y \
+ libdbus-1-dev libncurses5-dev libncursesw5-dev
+ - name: Build
+ run: cargo build --release && strip target/release/dijo
+
+ - name: Upload binaries to release
+ uses: svenstaro/upload-release-action@v1-release
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ file: target/release/dijo
+ asset_name: dijo-x86_64-linux
+ tag: ${{ github.ref }}
+ overwrite: true
+
+ build-apple:
+ runs-on: macos-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v1
+ - name: Cache Rust dependencies
+ uses: actions/cache@v1.0.1
+ with:
+ path: target
+ key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.OS }}-build-
+ - name: Install latest rust toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: beta
+ target: x86_64-apple-darwin
+ default: true
+ override: true
+
+ - name: Build for mac
+ run: cargo build --release && strip target/release/dijo
+
+ - name: Upload binaries to release
+ uses: svenstaro/upload-release-action@v1-release
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ file: target/release/dijo
+ asset_name: dijo-x86_64-apple
+ tag: ${{ github.ref }}
+ overwrite: true
+
+ build-windows:
+ runs-on: windows-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v1
+ - name: Cache Rust dependencies
+ uses: actions/cache@v1.0.1
+ with:
+ path: target
+ key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.OS }}-build-
+ - name: Install latest rust toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: beta
+ target: x86_64-pc-windows-msvc
+ default: true
+ override: true
+
+ - name: Build for windows
+ shell: bash
+ run: cargo build --release --no-default-features --features "crossterm-backend"
+
+ - name: Upload binaries to release
+ uses: svenstaro/upload-release-action@v1-release
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ file: target/release/dijo.exe
+ asset_name: dijo-x86_64-windows.exe
+ tag: ${{ github.ref }}
+ overwrite: true
diff --git a/Cargo.lock b/Cargo.lock
index ade0cc4..fd66bcd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -125,6 +125,15 @@ dependencies = [
]
[[package]]
+name = "cloudabi"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
name = "const-random"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -152,12 +161,12 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
[[package]]
name = "crossbeam-channel"
-version = "0.4.2"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061"
+checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6"
dependencies = [
+ "cfg-if",
"crossbeam-utils",
- "maybe-uninit",
]
[[package]]
@@ -172,6 +181,31 @@ dependencies = [
]
[[package]]
+name = "crossterm"
+version = "0.17.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f4919d60f26ae233e14233cc39746c8c8bb8cd7b05840ace83604917b51b6c7"
+dependencies = [
+ "bitflags",
+ "crossterm_winapi",
+ "lazy_static",
+ "libc",
+ "mio 0.7.0",
+ "parking_lot",
+ "signal-hook",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "crossterm_winapi"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "057b7146d02fb50175fd7dbe5158f6097f33d02831f43b4ee8ae4ddf67b68f5c"
+dependencies = [
+ "winapi 0.3.9",
+]
+
+[[package]]
name = "ctor"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -190,6 +224,7 @@ dependencies = [
"ahash 0.3.8",
"cfg-if",
"crossbeam-channel",
+ "crossterm",
"cursive_core",
"enumset",
"lazy_static",
@@ -260,7 +295,7 @@ dependencies = [
[[package]]
name = "dijo"
-version = "0.1.4"
+version = "0.2.3"
dependencies = [
"chrono",
"clap",
@@ -271,6 +306,7 @@ dependencies = [
"notify",
"serde",
"serde_json",
+ "toml",
"typetag",
]
@@ -522,6 +558,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9"
[[package]]
+name = "lock_api"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
name = "log"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -531,12 +576,6 @@ dependencies = [
]
[[package]]
-name = "maybe-uninit"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
-
-[[package]]
name = "mio"
version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -549,13 +588,27 @@ dependencies = [
"kernel32-sys",
"libc",
"log",
- "miow",
+ "miow 0.2.1",
"net2",
"slab",
"winapi 0.2.8",
]
[[package]]
+name = "mio"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e9971bc8349a361217a8f2a41f5d011274686bd4436465ba51730921039d7fb"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "log",
+ "miow 0.3.5",
+ "ntapi",
+ "winapi 0.3.9",
+]
+
+[[package]]
name = "mio-extras"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -563,7 +616,7 @@ checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19"
dependencies = [
"lazycell",
"log",
- "mio",
+ "mio 0.6.22",
"slab",
]
@@ -580,6 +633,16 @@ dependencies = [
]
[[package]]
+name = "miow"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07b88fb9795d4d36d62a012dfbf49a8f5cf12751f36d31a9dbe66d528e58979e"
+dependencies = [
+ "socket2",
+ "winapi 0.3.9",
+]
+
+[[package]]
name = "net2"
version = "0.2.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -602,13 +665,22 @@ dependencies = [
"fsevent-sys",
"inotify",
"libc",
- "mio",
+ "mio 0.6.22",
"mio-extras",
"walkdir",
"winapi 0.3.9",
]
[[package]]
+name = "ntapi"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a31937dea023539c72ddae0e3571deadc1414b300483fa7aaec176168cfa9d2"
+dependencies = [
+ "winapi 0.3.9",
+]
+
+[[package]]
name = "num"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -687,6 +759,30 @@ dependencies = [
]
[[package]]
+name = "parking_lot"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
+dependencies = [
+ "cfg-if",
+ "cloudabi",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "winapi 0.3.9",
+]
+
+[[package]]
name = "proc-macro-hack"
version = "0.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -694,9 +790,9 @@ checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
[[package]]
name = "proc-macro2"
-version = "1.0.18"
+version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
+checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
dependencies = [
"unicode-xid",
]
@@ -764,6 +860,12 @@ dependencies = [
]
[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
name = "serde"
version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -801,6 +903,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed"
dependencies = [
"libc",
+ "mio 0.7.0",
"signal-hook-registry",
]
@@ -821,6 +924,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
+name = "smallvec"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3757cb9d89161a2f24e1cf78efa0c1fcff485d18e3f55e0aa3480824ddaa0f3f"
+
+[[package]]
+name = "socket2"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "winapi 0.3.9",
+]
+
+[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -840,9 +961,9 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]]
name = "syn"
-version = "1.0.34"
+version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "936cae2873c940d92e697597c5eee105fb570cd5689c695806f672883653349b"
+checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0"
dependencies = [
"proc-macro2",
"quote",
@@ -881,6 +1002,15 @@ dependencies = [
]
[[package]]
+name = "toml"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
+dependencies = [
+ "serde",
+]
+
+[[package]]
name = "typetag"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 9cf3639..fc18bb9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "dijo"
-version = "0.1.4"
+version = "0.2.3"
authors = ["Akshay <nerdy@peppe.rs>"]
edition = "2018"
description = "Scriptable, curses-based, digital habit tracker"
@@ -19,11 +19,11 @@ typetag = "0.1.4"
directories = "3.0.1"
clap = "2.33"
notify = "4.0"
+toml = "0.5.6"
[dependencies.cursive]
version = "0.15"
default-features = false
-features = ["termion-backend"]
[dependencies.chrono]
version = "0.4"
@@ -32,3 +32,8 @@ features = ["serde"]
[dependencies.serde]
version = "1.0.103"
features = ["derive"]
+
+[features]
+default = ["termion-backend"]
+termion-backend = ["cursive/termion-backend"]
+crossterm-backend = ["cursive/crossterm-backend"]
diff --git a/readme.md b/readme.md
index fbca787..c626432 100644
--- a/readme.md
+++ b/readme.md
@@ -19,7 +19,7 @@ much like a certain text editor.
- **vim like command mode**: add with `:add`, delete with
`:delete` and above all, quit with `:q`!.
- **fully scriptable**: [configure `dijo` to
- track your `git` commits](./Auto-Habits)!
+ track your `git` commits](https://github.com/NerdyPepper/dijo/wiki/Auto-Habits)!
### Install
@@ -32,6 +32,12 @@ $ rustup update
$ cargo install dijo
```
+`dijo` on nixpkgs (maintained by [@Infinisil](https://github.com/Infinisil)):
+
+```
+$ nix-env -f channel:nixpkgs-unstable -iA dijo
+```
+
If you aren't familiar with `cargo` or Rust, read the [complete
installation](https://github.com/NerdyPepper/dijo/wiki/Install)
guide.
diff --git a/src/app/impl_self.rs b/src/app/impl_self.rs
index 744f906..5cd9616 100644
--- a/src/app/impl_self.rs
+++ b/src/app/impl_self.rs
@@ -13,8 +13,7 @@ use notify::{watcher, RecursiveMode, Watcher};
use crate::command::{Command, CommandLineError};
use crate::habit::{Bit, Count, HabitWrapper, TrackEvent, ViewMode};
-use crate::utils;
-use crate::CONFIGURATION;
+use crate::utils::{self, GRID_WIDTH, VIEW_HEIGHT, VIEW_WIDTH};
use crate::app::{App, MessageKind, StatusLine};
@@ -37,6 +36,10 @@ impl App {
self.habits.push(h);
}
+ pub fn list_habits(&self) -> Vec<String> {
+ self.habits.iter().map(|x| x.name()).collect::<Vec<_>>()
+ }
+
pub fn delete_by_name(&mut self, name: &str) {
let old_len = self.habits.len();
self.habits.retain(|h| h.name() != name);
@@ -83,7 +86,6 @@ impl App {
}
pub fn set_focus(&mut self, d: Absolute) {
- let grid_width = CONFIGURATION.grid_width;
match d {
Absolute::Right => {
if self.focus != self.habits.len() - 1 {
@@ -96,15 +98,15 @@ impl App {
}
}
Absolute::Down => {
- if self.focus + grid_width < self.habits.len() - 1 {
- self.focus += grid_width;
+ if self.focus + GRID_WIDTH < self.habits.len() - 1 {
+ self.focus += GRID_WIDTH;
} else {
self.focus = self.habits.len() - 1;
}
}
Absolute::Up => {
- if self.focus as isize - grid_width as isize >= 0 {
- self.focus -= grid_width;
+ if self.focus as isize - GRID_WIDTH as isize >= 0 {
+ self.focus -= GRID_WIDTH;
} else {
self.focus = 0;
}
@@ -118,13 +120,13 @@ impl App {
}
pub fn status(&self) -> StatusLine {
- let today = chrono::Local::now().naive_utc().date();
+ let today = chrono::Local::now().naive_local().date();
let remaining = self.habits.iter().map(|h| h.remaining(today)).sum::<u32>();
let total = self.habits.iter().map(|h| h.goal()).sum::<u32>();
let completed = total - remaining;
let timestamp = if self.view_month_offset == 0 {
- format!("{}", Local::now().date().format("%d/%b/%y"),)
+ format!("{}", Local::now().naive_local().date().format("%d/%b/%y"),)
} else {
let months = self.view_month_offset;
format!("{}", format!("{} months ago", months),)
@@ -142,12 +144,10 @@ impl App {
}
pub fn max_size(&self) -> Vec2 {
- let grid_width = CONFIGURATION.grid_width;
- let width = grid_width * CONFIGURATION.view_width;
+ let width = GRID_WIDTH * VIEW_WIDTH;
let height = {
if !self.habits.is_empty() {
- (CONFIGURATION.view_height as f64
- * (self.habits.len() as f64 / grid_width as f64).ceil())
+ (VIEW_HEIGHT as f64 * (self.habits.len() as f64 / GRID_WIDTH as f64).ceil())
as usize
} else {
0
@@ -207,12 +207,18 @@ impl App {
.iter_mut()
.find(|x| x.name() == name && x.is_auto());
if let Some(h) = target_habit {
- h.modify(Local::now().naive_utc().date(), event);
+ h.modify(Local::now().naive_local().date(), event);
}
};
match result {
Ok(c) => match c {
Command::Add(name, goal, auto) => {
+ if let Some(_) = self.habits.iter().find(|x| x.name() == name) {
+ self.message.set_kind(MessageKind::Error);
+ self.message
+ .set_message(format!("Habit `{}` already exist", &name));
+ return;
+ }
let kind = if goal == Some(1) { "bit" } else { "count" };
if kind == "count" {
self.add_habit(Box::new(Count::new(name, goal.unwrap_or(0), auto)));
@@ -230,7 +236,31 @@ impl App {
Command::TrackDown(name) => {
_track(&name, TrackEvent::Decrement);
}
- Command::Quit => self.save_state(),
+ Command::Help(input) => {
+ if let Some(topic) = input.as_ref().map(String::as_ref) {
+ self.message.set_message(
+ match topic {
+ "a" | "add" => "add <habit-name> [goal] (alias: a)",
+ "aa" | "add-auto" => "add-auto <habit-name> [goal] (alias: aa)",
+ "d" | "delete" => "delete <habit-name> (alias: d)",
+ "mprev" | "month-prev" => "month-prev (alias: mprev)",
+ "mnext" | "month-next" => "month-next (alias: mnext)",
+ "tup" | "track-up" => "track-up <auto-habit-name> (alias: tup)",
+ "tdown" | "track-down" => "track-down <auto-habit-name> (alias: tdown)",
+ "q" | "quit" => "quit dijo",
+ "w" | "write" => "write current state to disk (alias: w)",
+ "h"|"?" | "help" => "help [<command>|commands|keys] (aliases: h, ?)",
+ "cmds" | "commands" => "add, add-auto, delete, month-{prev,next}, track-{up,down}, help, quit",
+ "keys" => "TODO", // TODO (view?)
+ _ => "unknown command or help topic.",
+ }
+ )
+ } else {
+ // TODO (view?)
+ self.message.set_message("help <command>|commands|keys")
+ }
+ }
+ Command::Quit | Command::Write => self.save_state(),
Command::MonthNext => self.sift_forward(),
Command::MonthPrev => self.sift_backward(),
Command::Blank => {}
diff --git a/src/app/impl_view.rs b/src/app/impl_view.rs
index 892b00c..395cac4 100644
--- a/src/app/impl_view.rs
+++ b/src/app/impl_view.rs
@@ -12,21 +12,17 @@ use notify::DebouncedEvent;
use crate::app::{App, MessageKind};
use crate::habit::{HabitWrapper, ViewMode};
-use crate::utils;
-use crate::CONFIGURATION;
+use crate::utils::{self, GRID_WIDTH, VIEW_HEIGHT, VIEW_WIDTH};
impl View for App {
fn draw(&self, printer: &Printer) {
- let grid_width = CONFIGURATION.grid_width;
- let view_width = CONFIGURATION.view_width;
- let view_height = CONFIGURATION.view_height;
let mut offset = Vec2::zero();
for (idx, habit) in self.habits.iter().enumerate() {
- if idx >= grid_width && idx % grid_width == 0 {
- offset = offset.map_y(|y| y + view_height).map_x(|_| 0);
+ if idx >= GRID_WIDTH && idx % GRID_WIDTH == 0 {
+ offset = offset.map_y(|y| y + VIEW_HEIGHT).map_x(|_| 0);
}
habit.draw(&printer.offset(offset).focused(self.focus == idx));
- offset = offset.map_x(|x| x + view_width + 2);
+ offset = offset.map_x(|x| x + VIEW_WIDTH + 2);
}
offset = offset.map_x(|_| 0).map_y(|_| self.max_size().y - 2);
@@ -45,13 +41,10 @@ impl View for App {
}
fn required_size(&mut self, _: Vec2) -> Vec2 {
- let grid_width = CONFIGURATION.grid_width;
- let view_width = CONFIGURATION.view_width;
- let view_height = CONFIGURATION.view_height;
- let width = grid_width * (view_width + 2);
+ let width = GRID_WIDTH * (VIEW_WIDTH + 2);
let height = {
if self.habits.len() > 0 {
- (view_height as f64 * (self.habits.len() as f64 / grid_width as f64).ceil())
+ (VIEW_HEIGHT as f64 * (self.habits.len() as f64 / GRID_WIDTH as f64).ceil())
as usize
} else {
0
@@ -102,25 +95,6 @@ impl View for App {
self.set_focus(Absolute::Down);
return EventResult::Consumed(None);
}
- Event::Char('d') => {
- if self.habits.is_empty() {
- return EventResult::Consumed(None);
- }
- self.habits.remove(self.focus);
- self.focus = self.focus.checked_sub(1).unwrap_or(0);
- return EventResult::Consumed(None);
- }
- Event::Char('w') => {
- // helper bind to test write to file
- let j = serde_json::to_string_pretty(&self.habits).unwrap();
- let mut file = File::create("foo.txt").unwrap();
- file.write_all(j.as_bytes()).unwrap();
- return EventResult::Consumed(None);
- }
- Event::Char('q') => {
- self.save_state();
- return EventResult::with_cb(|s| s.quit());
- }
Event::Char('v') => {
if self.habits.is_empty() {
return EventResult::Consumed(None);
diff --git a/src/command.rs b/src/command.rs
index f856b00..4f3e491 100644
--- a/src/command.rs
+++ b/src/command.rs
@@ -1,21 +1,73 @@
use std::fmt;
+use cursive::event::{Event, EventResult, Key};
use cursive::theme::{BaseColor, Color, ColorStyle};
use cursive::view::Resizable;
-use cursive::views::{EditView, LinearLayout, TextView};
+use cursive::views::{EditView, LinearLayout, OnEventView, TextView};
use cursive::Cursive;
-use crate::{app::App, CONFIGURATION};
+use crate::app::App;
+use crate::utils::{GRID_WIDTH, VIEW_WIDTH};
+
+static COMMANDS: &'static [&'static str] = &[
+ "add",
+ "add-auto",
+ "delete",
+ "track-up",
+ "track-down",
+ "month-prev",
+ "month-next",
+ "quit",
+ "write",
+ "help",
+];
+
+fn get_command_completion(prefix: &str) -> Option<String> {
+ let first_match = COMMANDS.iter().filter(|&x| x.starts_with(prefix)).next();
+ return first_match.map(|&x| x.into());
+}
+
+fn get_habit_completion(prefix: &str, habit_names: &[String]) -> Option<String> {
+ let first_match = habit_names.iter().filter(|&x| x.starts_with(prefix)).next();
+ return first_match.map(|x| x.into());
+}
pub fn open_command_window(s: &mut Cursive) {
- let command_window = EditView::new()
- .filler(" ")
- .on_submit(call_on_app)
- .style(ColorStyle::new(
- Color::Dark(BaseColor::Black),
- Color::Dark(BaseColor::White),
- ))
- .fixed_width(CONFIGURATION.view_width * CONFIGURATION.grid_width);
+ let habit_list: Vec<String> = s
+ .call_on_name("Main", |view: &mut App| {
+ return view.list_habits();
+ })
+ .unwrap();
+ let style = ColorStyle::new(Color::Dark(BaseColor::Black), Color::Dark(BaseColor::White));
+ let command_window = OnEventView::new(
+ EditView::new()
+ .filler(" ")
+ .on_submit(call_on_app)
+ .style(style),
+ )
+ .on_event_inner(
+ Event::Key(Key::Tab),
+ move |view: &mut EditView, _: &Event| {
+ let contents = view.get_content();
+ if !contents.contains(" ") {
+ let completion = get_command_completion(&*contents);
+ if let Some(c) = completion {
+ let cb = view.set_content(c);
+ return Some(EventResult::Consumed(Some(cb)));
+ };
+ return None;
+ } else {
+ let word = contents.split(' ').last().unwrap();
+ let completion = get_habit_completion(word, &habit_list);
+ if let Some(c) = completion {
+ let cb = view.set_content(format!("{}", contents) + &c[word.len()..]);
+ return Some(EventResult::Consumed(Some(cb)));
+ };
+ return None;
+ }
+ },
+ )
+ .fixed_width(VIEW_WIDTH * GRID_WIDTH);
s.call_on_name("Frame", |view: &mut LinearLayout| {
let mut commandline = LinearLayout::horizontal()
.child(TextView::new(":"))
@@ -59,15 +111,17 @@ pub enum Command {
Delete(String),
TrackUp(String),
TrackDown(String),
+ Help(Option<String>),
+ Write,
Quit,
Blank,
}
#[derive(Debug)]
pub enum CommandLineError {
- InvalidCommand(String),
- InvalidArg(u32), // position
- NotEnoughArgs(String, u32),
+ InvalidCommand(String), // command name
+ InvalidArg(u32), // position
+ NotEnoughArgs(String, u32), // command name, required no. of args
}
impl std::error::Error for CommandLineError {}
@@ -134,9 +188,16 @@ impl Command {
}
return Ok(Command::TrackDown(args[0].to_string()));
}
+ "h" | "?" | "help" => {
+ if args.is_empty() {
+ return Ok(Command::Help(None));
+ }
+ return Ok(Command::Help(Some(args[0].to_string())));
+ }
"mprev" | "month-prev" => return Ok(Command::MonthPrev),
"mnext" | "month-next" => return Ok(Command::MonthNext),
"q" | "quit" => return Ok(Command::Quit),
+ "w" | "write" => return Ok(Command::Write),
"" => return Ok(Command::Blank),
s => return Err(CommandLineError::InvalidCommand(s.into())),
}
diff --git a/src/habit/bit.rs b/src/habit/bit.rs
index 8fa14c2..2bbb0ac 100644
--- a/src/habit/bit.rs
+++ b/src/habit/bit.rs
@@ -18,9 +18,9 @@ impl fmt::Display for CustomBool {
f,
"{:^3}",
if self.0 {
- CONFIGURATION.true_chr
+ CONFIGURATION.look.true_chr
} else {
- CONFIGURATION.false_chr
+ CONFIGURATION.look.false_chr
}
)
}
diff --git a/src/main.rs b/src/main.rs
index 14ddf03..5280b35 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -12,7 +12,13 @@ use crate::command::{open_command_window, Command};
use crate::utils::{load_configuration_file, AppConfig};
use clap::{App as ClapApp, Arg};
+
+#[cfg(any(feature = "termion-backend", feature = "default"))]
use cursive::termion;
+
+#[cfg(feature = "crossterm-backend")]
+use cursive::crossterm;
+
use cursive::views::{LinearLayout, NamedView};
use lazy_static::lazy_static;
@@ -33,6 +39,14 @@ fn main() {
.value_name("CMD")
.help("run a dijo command"),
)
+ .arg(
+ Arg::with_name("list")
+ .short("l")
+ .long("list")
+ .takes_value(false)
+ .help("list dijo habits")
+ .conflicts_with("command"),
+ )
.get_matches();
if let Some(c) = matches.value_of("command") {
let command = Command::from_string(c);
@@ -49,8 +63,17 @@ fn main() {
"Commands other than `track-up` and `track-down` are currently not supported!"
),
}
+ } else if matches.is_present("list") {
+ for h in App::load_state().list_habits() {
+ println!("{}", h);
+ }
} else {
+ #[cfg(any(feature = "termion-backend", feature = "default"))]
let mut s = termion().unwrap();
+
+ #[cfg(feature = "crossterm-backend")]
+ let mut s = crossterm().unwrap();
+
let app = App::load_state();
let layout = NamedView::new(
"Frame",
diff --git a/src/theme.rs b/src/theme.rs
index 4194777..1d2cc36 100644
--- a/src/theme.rs
+++ b/src/theme.rs
@@ -1,18 +1,18 @@
use cursive::theme::Color::*;
use cursive::theme::PaletteColor::*;
-use cursive::theme::{BaseColor, BorderStyle, Palette, Theme};
+use cursive::theme::{BorderStyle, Palette, Theme};
pub fn pallete_gen() -> Palette {
let mut p = Palette::default();
- p[Background] = Dark(BaseColor::Black);
- p[Shadow] = Light(BaseColor::Black);
- p[View] = Dark(BaseColor::Black);
- p[Primary] = Dark(BaseColor::White);
- p[Secondary] = Dark(BaseColor::Black);
- p[Tertiary] = Dark(BaseColor::Green);
- p[TitlePrimary] = Light(BaseColor::White);
- p[Highlight] = Dark(BaseColor::Red);
- p[HighlightInactive] = Dark(BaseColor::Black);
+ p[Background] = TerminalDefault;
+ p[Shadow] = TerminalDefault;
+ p[View] = TerminalDefault;
+ p[Primary] = TerminalDefault;
+ p[Secondary] = TerminalDefault;
+ p[Tertiary] = TerminalDefault;
+ p[TitlePrimary] = TerminalDefault;
+ p[Highlight] = TerminalDefault;
+ p[HighlightIna