Command-line and Emacs Calendar Client

Compare changes

Choose any two refs to compare.

+7 -1
README.md
···
-
# ๐Ÿ“… Caledonia ๐Ÿด๓ ง๓ ข๓ ณ๓ ฃ๓ ด๓ ฟ
+
# Caledonia
Caledonia is a calendar client with command-line and Emacs front-ends.
It operates on a [vdir](https://pimutils.org/specs/vdir/) directory of [`.ics`](https://datatracker.ietf.org/doc/html/rfc5545) files as managed by tools like [vdirsyncer](https://github.com/pimutils/vdirsyncer), which allows it to interact with CalDAV servers.
···
```
$ opam install .
+
```
+
+
With [Nix](https://nixos.org/),
+
+
```
+
$ nix shell 'git+https://tangled.sh/@ryan.freumh.org/caledonia?ref=main'
```
## Configuration
+33 -41
TODO.org
···
-
- [x] list/search events
-
- [x] add/remove events
-
- [x] edit events
-
- [x] timezones
-
- [x] remove collection module
-
- ref [[https://github.com/ocaml-ppx/ppxlib/issues/481]] cc patrick
-
- [ ] add timezone to call date functions and make it required
-
- [ ] allow editing recurrence-ids
-
- [ ] don't load all calendars into memory to show only one event
-
- [ ] support specifying duration
-
- [ ] better add/edit event datetime parsing
-
- day of the week
-
- month names
-
- allow editting date or time without touching the other
-
- NB in emacs we use org-mode's datetime picker
-
- [x] diagnose events failing to parse [[https://github.com/robur-coop/icalendar/issues/14]]
-
- [x] [[https://github.com/robur-coop/icalendar/pull/13][handle RECURRENCE-ID]]
-
- [x] [[https://github.com/robur-coop/icalendar/issues/15][RRULE with local datetime]]
-
- [ ] CalDAV syncing
-
- Currently, you can use [[https://github.com/pimutils/vdirsyncer][vdirsyncer]]
-
- [ ] support querying times as well as dates
-
- [ ] custom date/time formatting
-
- [ ] support querying regex
-
- [ ] support VTIMEZONE
-
- [ ] support VALARMS
-
- [ ] support VOTODS
-
- [ ] support VCARDS
-
- [x] server mode
-
- [x] hold =Event='s in-memory instead of parsing them for every =Query=
-
- [ ] implement TUI front end with something like [[https://github.com/leostera/minttea][minttea]]
-
- [ ] implement an emacs front end, like mu4e to mu
-
- [x] listing, searching, querying
-
- [x] show details
-
- [x] show file
-
- [x] refresh
-
- [x] list possible calendars
-
- [x] add functions and bindings to change query parameters on the fly
-
- [ ] timezone support
-
- [ ] check the date module
-
- [ ] support adding, deleting, and editing events
-
- [ ] don't print end date if it's the same as the start date (will require a different sort function)
+
* DONE list/search events
+
* DONE add/remove events
+
* DONE edit events
+
* DONE timezones
+
* DONE diagnose events failing to parse [[https://github.com/robur-coop/icalendar/issues/14]]
+
* DONE [[https://github.com/robur-coop/icalendar/pull/13][handle RECURRENCE-ID]]
+
* DONE [[https://github.com/robur-coop/icalendar/issues/15][RRULE with local datetime]]
+
* DONE server mode
+
* DONE hold =Event='s in-memory instead of parsing them for every =Query=
+
* TODO support vdir metadata
+
* TODO add timezone all date functions
+
* TODO allow editing recurrence-ids
+
* TODO support specifying duration
+
* TODO CalDAV syncing
+
* TODO support complex datetime queries
+
* TODO custom date/time formatting
+
* TODO support querying text with regex
+
* TODO support VTIMEZONE
+
* TODO support VALARMS
+
* TODO support VOTODS
+
* TODO support VCARDS
+
* TODO support VJOURNAL
+
* TODO implement TUI front end with something like [[https://github.com/leostera/minttea][minttea]]
+
* TODO implement an emacs front end, like mu4e to mu
+
** DONE listing, searching, querying
+
** DONE show details
+
** DONE show file
+
** DONE refresh
+
** DONE list possible calendars
+
** DONE add functions and bindings to change query parameters on the fly
+
** TODO timezone support
+
** TODO support adding, deleting, and editing events
+
** TODO add a org-agenda style view for caledonia-list
+1
caledonia.opam
···
"eio_main" {>= "0.12"}
"timere" {>= "0.8.0"}
"timedesc" {>= "0.8.0"}
+
"ppx_sexp_conv" {>= "0.15.1"}
"alcotest" {>= "1.8.0" & with-test}
]
pin-depends: [
+175
flake.lock
···
+
{
+
"nodes": {
+
"flake-compat": {
+
"flake": false,
+
"locked": {
+
"lastModified": 1696426674,
+
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
+
"owner": "edolstra",
+
"repo": "flake-compat",
+
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+
"type": "github"
+
},
+
"original": {
+
"owner": "edolstra",
+
"repo": "flake-compat",
+
"type": "github"
+
}
+
},
+
"flake-utils": {
+
"inputs": {
+
"systems": "systems"
+
},
+
"locked": {
+
"lastModified": 1731533236,
+
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+
"owner": "numtide",
+
"repo": "flake-utils",
+
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+
"type": "github"
+
},
+
"original": {
+
"owner": "numtide",
+
"repo": "flake-utils",
+
"type": "github"
+
}
+
},
+
"mirage-opam-overlays": {
+
"flake": false,
+
"locked": {
+
"lastModified": 1710922379,
+
"narHash": "sha256-j4QREQDUf8oHOX7qg6wAOupgsNQoYlufxoPrgagD+pY=",
+
"owner": "dune-universe",
+
"repo": "mirage-opam-overlays",
+
"rev": "797cb363df3ff763c43c8fbec5cd44de2878757e",
+
"type": "github"
+
},
+
"original": {
+
"owner": "dune-universe",
+
"repo": "mirage-opam-overlays",
+
"type": "github"
+
}
+
},
+
"nixpkgs": {
+
"locked": {
+
"lastModified": 1743761221,
+
"narHash": "sha256-g79y3RpbyfOlwm3W4yuMXLmmNI7iDgIDqg10nq8pz5U=",
+
"owner": "nixos",
+
"repo": "nixpkgs",
+
"rev": "e1964791d66993a8aa5b9f19c1c6dd5827851355",
+
"type": "github"
+
},
+
"original": {
+
"owner": "nixos",
+
"repo": "nixpkgs",
+
"type": "github"
+
}
+
},
+
"opam-nix": {
+
"inputs": {
+
"flake-compat": "flake-compat",
+
"flake-utils": [
+
"flake-utils"
+
],
+
"mirage-opam-overlays": "mirage-opam-overlays",
+
"nixpkgs": [
+
"nixpkgs"
+
],
+
"opam-overlays": "opam-overlays",
+
"opam-repository": "opam-repository",
+
"opam2json": "opam2json"
+
},
+
"locked": {
+
"lastModified": 1744376747,
+
"narHash": "sha256-CKZLqLgZtMrUyZKlroISYp6Z4aoN1N9xeyk0dPNxGvc=",
+
"owner": "RyanGibb",
+
"repo": "opam-nix",
+
"rev": "787eeba962582ff0142f445a74f1edb667d7e941",
+
"type": "github"
+
},
+
"original": {
+
"owner": "RyanGibb",
+
"ref": "timere",
+
"repo": "opam-nix",
+
"type": "github"
+
}
+
},
+
"opam-overlays": {
+
"flake": false,
+
"locked": {
+
"lastModified": 1726822209,
+
"narHash": "sha256-bwM18ydNT9fYq91xfn4gmS21q322NYrKwfq0ldG9GYw=",
+
"owner": "dune-universe",
+
"repo": "opam-overlays",
+
"rev": "f2bec38beca4aea9e481f2fd3ee319c519124649",
+
"type": "github"
+
},
+
"original": {
+
"owner": "dune-universe",
+
"repo": "opam-overlays",
+
"type": "github"
+
}
+
},
+
"opam-repository": {
+
"flake": false,
+
"locked": {
+
"lastModified": 1740730647,
+
"narHash": "sha256-6veU2WjUGcWDAzLDjoAI1L6GWZd0KIUq19sHcbJS+u8=",
+
"owner": "ocaml",
+
"repo": "opam-repository",
+
"rev": "f1f75fef5fbf1e8bd1cc9544e50b89ba59f625e2",
+
"type": "github"
+
},
+
"original": {
+
"owner": "ocaml",
+
"repo": "opam-repository",
+
"type": "github"
+
}
+
},
+
"opam2json": {
+
"inputs": {
+
"nixpkgs": [
+
"opam-nix",
+
"nixpkgs"
+
]
+
},
+
"locked": {
+
"lastModified": 1671540003,
+
"narHash": "sha256-5pXfbUfpVABtKbii6aaI2EdAZTjHJ2QntEf0QD2O5AM=",
+
"owner": "tweag",
+
"repo": "opam2json",
+
"rev": "819d291ea95e271b0e6027679de6abb4d4f7f680",
+
"type": "github"
+
},
+
"original": {
+
"owner": "tweag",
+
"repo": "opam2json",
+
"type": "github"
+
}
+
},
+
"root": {
+
"inputs": {
+
"flake-utils": "flake-utils",
+
"nixpkgs": "nixpkgs",
+
"opam-nix": "opam-nix"
+
}
+
},
+
"systems": {
+
"locked": {
+
"lastModified": 1681028828,
+
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+
"owner": "nix-systems",
+
"repo": "default",
+
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+
"type": "github"
+
},
+
"original": {
+
"owner": "nix-systems",
+
"repo": "default",
+
"type": "github"
+
}
+
}
+
},
+
"root": "root",
+
"version": 7
+
}
+39
flake.nix
···
+
{
+
inputs = {
+
nixpkgs.url = "github:nixos/nixpkgs";
+
opam-nix.url = "github:tweag/opam-nix";
+
flake-utils.url = "github:numtide/flake-utils";
+
# we pin opam-nix's nixpkgs to follow the flakes, avoiding using two different instances
+
opam-nix.inputs.nixpkgs.follows = "nixpkgs";
+
# deduplicate flakes
+
opam-nix.inputs.flake-utils.follows = "flake-utils";
+
};
+
outputs = { self, nixpkgs, flake-utils, opam-nix, ... }@inputs:
+
flake-utils.lib.eachDefaultSystem (system:
+
let
+
package = "caledonia";
+
pkgs = nixpkgs.legacyPackages.${system};
+
opam-nix-lib = opam-nix.lib.${system};
+
devPackagesQuery = {
+
ocaml-lsp-server = "*";
+
ocamlformat = "*";
+
utop = "*";
+
};
+
query = {
+
ocaml-base-compiler = "*";
+
};
+
scope =
+
opam-nix-lib.buildOpamProject' { } ./. (query // devPackagesQuery);
+
in {
+
packages.default = scope.${package};
+
defaultPackage = scope.${package};
+
+
devShells.default = let
+
devPackages = builtins.attrValues
+
(pkgs.lib.getAttrs (builtins.attrNames devPackagesQuery) scope);
+
in pkgs.mkShell {
+
inputsFrom = [ scope.${package} ];
+
buildInputs = devPackages;
+
};
+
});
+
}