diff options
-rw-r--r-- | .github/workflows/main.yml | 118 | ||||
-rw-r--r-- | Cargo.lock | 164 | ||||
-rw-r--r-- | Cargo.toml | 9 | ||||
-rw-r--r-- | readme.md | 8 | ||||
-rw-r--r-- | src/app/impl_self.rs | 60 | ||||
-rw-r--r-- | src/app/impl_view.rs | 38 | ||||
-rw-r--r-- | src/command.rs | 87 | ||||
-rw-r--r-- | src/habit/bit.rs | 4 | ||||
-rw-r--r-- | src/main.rs | 23 | ||||
-rw-r--r-- | src/theme.rs | 20 | ||||
-rw-r--r-- | src/utils.rs | 130 | ||||
-rw-r--r-- | src/views.rs | 35 |
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 @@ -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" @@ -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"] @@ -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 |