mirror of
https://github.com/tomasriveral/NoteWrapper.git
synced 2026-10-10 17:03:04 +02:00
Compare commits
129
Commits
v1.1
...
git-backup
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f37e48030 | ||
|
|
df0301fdcd | ||
|
|
518d4fd09c | ||
|
|
a83da3e1aa | ||
|
|
eb0d6b6e5d | ||
|
|
82d016fdd7 | ||
|
|
2e03edac2b | ||
|
|
689c3074c0 | ||
|
|
e54933e5fa | ||
|
|
3f9471531d | ||
|
|
ab1928551a | ||
|
|
e75c8e5815 | ||
|
|
693e85d974 | ||
|
|
b9f71f580a | ||
|
|
c6aac80951 | ||
|
|
b395fe1072 | ||
|
|
27cc3de27d | ||
|
|
18538f3250 | ||
|
|
6c7cc413da | ||
|
|
e670ef935f | ||
|
|
29d86955f4 | ||
|
|
0f9ec32bfc | ||
|
|
5ad8f2f99e | ||
|
|
c6e590e57c | ||
|
|
d47bdb82eb | ||
|
|
50748eeac0 | ||
|
|
19a8f7e07f | ||
|
|
1c17c62835 | ||
|
|
e666ca32de | ||
|
|
2a8392ac2e | ||
|
|
292688296e | ||
|
|
4c18c2dda4 | ||
|
|
1bfb7f82a5 | ||
|
|
a32f97d229 | ||
|
|
6555b7247e | ||
|
|
2b2a3455d2 | ||
|
|
d8a11bbc16 | ||
|
|
1d95983d5d | ||
|
|
30449ca473 | ||
|
|
7af79c76b1 | ||
|
|
27b8b310cb | ||
|
|
4d228fb911 | ||
|
|
34a2546990 | ||
|
|
7231741f2d | ||
|
|
e86d7e7e03 | ||
|
|
280c329339 | ||
|
|
f7c84136a5 | ||
|
|
f66371137a | ||
|
|
77b5148489 | ||
|
|
d537357802 | ||
|
|
503dddd9be | ||
|
|
fd77eaa4de | ||
|
|
d6fa89785f | ||
|
|
30616b1b6f | ||
|
|
004b536e05 | ||
|
|
c9ee6ca032 | ||
|
|
c8b5538e1c | ||
|
|
2a09d4ee46 | ||
|
|
52169ccbe4 | ||
|
|
31366aeb48 | ||
|
|
e370f98c5e | ||
|
|
5c4bd6ef16 | ||
|
|
6e6ddebb5e | ||
|
|
fcb853e36a | ||
|
|
91d6db6c74 | ||
|
|
833f8c1bd2 | ||
|
|
5328fdc91c | ||
|
|
f85a32f97f | ||
|
|
bc306ca630 | ||
|
|
89c686652e | ||
|
|
d1c19696ff | ||
|
|
8ee0096540 | ||
|
|
3fa0dbe0dd | ||
|
|
2d6787a91b | ||
|
|
2f688df3ec | ||
|
|
1f20eb6996 | ||
|
|
ebd1ea70e1 | ||
|
|
ffcc16c14f | ||
|
|
9aa8b075e9 | ||
|
|
9a865897ed | ||
|
|
5cdf27f7ed | ||
|
|
e98985fb5b | ||
|
|
dff2fb25aa | ||
|
|
56498b2732 | ||
|
|
73f892aff0 | ||
|
|
533afd0a4b | ||
|
|
986618a05b | ||
|
|
7bd39a08c6 | ||
|
|
37a0763e14 | ||
|
|
0bf3d4624e | ||
|
|
36189b4a9d | ||
|
|
feadf62632 | ||
|
|
a839b0da5b | ||
|
|
027038e49e | ||
|
|
0b2c499bd1 | ||
|
|
8720a17286 | ||
|
|
c46ff838c1 | ||
|
|
66ca150190 | ||
|
|
acd64a845b | ||
|
|
2a5b08b737 | ||
|
|
a142f92c79 | ||
|
|
4422a8d6fd | ||
|
|
fc9480e12e | ||
|
|
d1561ddc2e | ||
|
|
a33a94637f | ||
|
|
4d7c44c016 | ||
|
|
433aca0a58 | ||
|
|
f8bc4004ed | ||
|
|
bf32877f96 | ||
|
|
99e0f9c130 | ||
|
|
92caf3ffd7 | ||
|
|
a3ece38fc1 | ||
|
|
aa4fa234ff | ||
|
|
d5b5d07e64 | ||
|
|
3fb2a32b83 | ||
|
|
5c87ac0aa2 | ||
|
|
3233b330db | ||
|
|
b7ca3eca14 | ||
|
|
68991673c0 | ||
|
|
0d2ec12263 | ||
|
|
107519507f | ||
|
|
e2343dd401 | ||
|
|
08fffcb3d1 | ||
|
|
8cae6cf8ff | ||
|
|
495e8015bc | ||
|
|
d208ccaa20 | ||
|
|
ce4b5cd719 | ||
|
|
f6625e0ef1 | ||
|
|
ae168f92ce |
No files matched your search
@@ -0,0 +1,3 @@
|
|||||||
|
BasedOnStyle: LLVM
|
||||||
|
IndentWidth: 4
|
||||||
|
ColumnLimit: 210
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
name: Release PR
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release_pr:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Configure git
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
|
||||||
|
- uses: DeterminateSystems/nix-installer-action@v12
|
||||||
|
|
||||||
|
- name: Create release branch
|
||||||
|
run: |
|
||||||
|
git checkout -B release/${{ inputs.version }}
|
||||||
|
|
||||||
|
- name: Set version in flake.nix
|
||||||
|
run: |
|
||||||
|
sed -i "s/version = \".*\";/version = \"${{ inputs.version }}\";/" flake.nix
|
||||||
|
|
||||||
|
- name: Update flake inputs
|
||||||
|
run: nix flake update
|
||||||
|
|
||||||
|
- name: Commit changes
|
||||||
|
run: |
|
||||||
|
git add flake.nix flake.lock
|
||||||
|
git commit -m "chore(release): ${{ inputs.version }}"
|
||||||
|
|
||||||
|
- name: Push branch
|
||||||
|
run: |
|
||||||
|
git push origin release/${{ inputs.version }}
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
run: |
|
||||||
|
gh pr create \
|
||||||
|
--base main \
|
||||||
|
--head release/${{ inputs.version }} \
|
||||||
|
--title "[Automatic] Release PR ${{ inputs.version }}" \
|
||||||
|
--body "Automated release PR.\nAfter the test's succesfull run, create manually a release with a new tag matching ${{ inputs.version }}"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
name: test
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install clang
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y clang-format
|
||||||
|
- name: Check format
|
||||||
|
run: |
|
||||||
|
clang-format --version
|
||||||
|
find src -name "*.c" -o -name "*.h" | xargs clang-format --dry-run --Werror
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get install -y \
|
||||||
|
gcc \
|
||||||
|
make \
|
||||||
|
pkg-config \
|
||||||
|
libcjson-dev \
|
||||||
|
libncurses-dev \
|
||||||
|
libgit2-dev \
|
||||||
|
git \
|
||||||
|
neovim
|
||||||
|
- name: Prepare environnement
|
||||||
|
run: mkdir -p ~/Documents/Notes
|
||||||
|
- name: Build
|
||||||
|
run: make
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
export TERM=xterm
|
||||||
|
make test
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
name: Weekly Flake Update
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 3 * * 1" # every Monday at 03:00 UTC
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
flake_update:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: DeterminateSystems/nix-installer-action@v12
|
||||||
|
|
||||||
|
- name: Set branch name (date)
|
||||||
|
id: vars
|
||||||
|
run: |
|
||||||
|
echo "branch=flake-update/$(date +%Y-%m-%d)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Create update branch
|
||||||
|
run: |
|
||||||
|
git checkout -B ${{ steps.vars.outputs.branch }}
|
||||||
|
|
||||||
|
- name: Update flake inputs
|
||||||
|
run: |
|
||||||
|
nix flake update
|
||||||
|
|
||||||
|
- name: Commit changes
|
||||||
|
run: |
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git add flake.lock
|
||||||
|
git commit -m "chore: weekly flake update" || echo "No changes to commit"
|
||||||
|
|
||||||
|
- name: Push branch
|
||||||
|
run: |
|
||||||
|
git push origin ${{ steps.vars.outputs.branch }} --force
|
||||||
|
|
||||||
|
- name: Install GitHub CLI
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y gh
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
run: |
|
||||||
|
gh pr create \
|
||||||
|
--base main \
|
||||||
|
--head ${{ steps.vars.outputs.branch }} \
|
||||||
|
--title "[Automated] Weekly flake update $(date +%Y-%m-%d)" \
|
||||||
|
--body "Automated weekly update of flake inputs for $(date +%Y-%m-%d)."
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -8,3 +8,4 @@ result
|
|||||||
massif.out.*
|
massif.out.*
|
||||||
*.out.*
|
*.out.*
|
||||||
vgcore.*
|
vgcore.*
|
||||||
|
.direnv/
|
||||||
+20
-2
@@ -37,7 +37,7 @@ Format:
|
|||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
* `Editor (#1): add support for Microsoft Word`
|
* `Editor (#1): add support for Microsoft Word`
|
||||||
* `README.md: fix typos`
|
* `docs: fix typos in README.md`
|
||||||
* `Journal: improve entry parsing logic`
|
* `Journal: improve entry parsing logic`
|
||||||
|
|
||||||
Keep commit messages short and descriptive.
|
Keep commit messages short and descriptive.
|
||||||
@@ -58,7 +58,7 @@ To add support for a new editor, you must update both documentation and source c
|
|||||||
* Add the editor name to `supportedEditor[]`
|
* Add the editor name to `supportedEditor[]`
|
||||||
* Increment `numEditor`
|
* Increment `numEditor`
|
||||||
|
|
||||||
* If the editor executable name differs from its logical name (for example `neovim` → `nvim`), update `doesEditorExist()` in `src/utils.c`
|
* If the editor executable name differs from its logical name (for example `neovim` → `nvim`), update `isEditorValid()` in `src/utils.c`
|
||||||
|
|
||||||
* Modify `openEditor()` in `src/utils.c`:
|
* Modify `openEditor()` in `src/utils.c`:
|
||||||
|
|
||||||
@@ -67,6 +67,15 @@ To add support for a new editor, you must update both documentation and source c
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Changing default configuration
|
||||||
|
|
||||||
|
If you want to change the default configuration or add a new option:
|
||||||
|
1. Update [README's documentation](./README.md#configuration)
|
||||||
|
2. In `./src/utils.c`, update the function `initAppFilesAndDirs()` which creates a default configuration if `~/.config/notewrapper/config.json` or if neither `-c` nor `--config` is set.
|
||||||
|
3. In `./src/main.c` where the JSON is parsed, add the parsing logic. Do not forget the debugging information, errors when wrong type or when an important field is missing and add a default value if a less important field is missing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Before opening a pull request
|
## Before opening a pull request
|
||||||
|
|
||||||
Before submitting a pull request, ensure that:
|
Before submitting a pull request, ensure that:
|
||||||
@@ -76,3 +85,12 @@ Before submitting a pull request, ensure that:
|
|||||||
* `make`, or
|
* `make`, or
|
||||||
* `nix-build` (on NixOS)
|
* `nix-build` (on NixOS)
|
||||||
* There are **no warnings or errors**
|
* There are **no warnings or errors**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Continuous integration
|
||||||
|
|
||||||
|
All PR will need to pass these checks:
|
||||||
|
1. `clang-format`. Running `find src -name "*.c" -o -name "*.h" | xargs clang-format -i` will automatically apply those rules.
|
||||||
|
2. The program must build without warning or error.
|
||||||
|
3. The program must run for five seconds without crashing.
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
# NoteWrapper
|
# NoteWrapper
|
||||||
|
|
||||||
|
Note: This project fullfills all my usecases for now, so my main source for features ideas dried out. So if you have good ideas, feel free to open an issue ;)
|
||||||
|
|
||||||
[](https://deepwiki.com/tomasriveral/notewrapper)
|
[](https://deepwiki.com/tomasriveral/notewrapper)
|
||||||
|
|
||||||
## Why?
|
## Why?
|
||||||
@@ -21,7 +23,7 @@ The goal was to create a terminal-based interface for accessing vaults and notes
|
|||||||
* Standard Markdown files (no custom format)
|
* Standard Markdown files (no custom format)
|
||||||
* Journal support with flexible entry formats
|
* Journal support with flexible entry formats
|
||||||
* Real-time Markdown rendering via Vivify
|
* Real-time Markdown rendering via Vivify
|
||||||
* Backup support using `rsync`
|
* Backup different directories into different destinations
|
||||||
* Supports multiple editors
|
* Supports multiple editors
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -39,10 +41,12 @@ Before building NoteWrapper, you must install the following dependencies:
|
|||||||
* `sed`
|
* `sed`
|
||||||
* `ripgrep`
|
* `ripgrep`
|
||||||
* `fzf`
|
* `fzf`
|
||||||
|
* `libgit2`
|
||||||
|
|
||||||
You must also have a [supported editor (and their associated plugin if needed)](#editor-support) installed:
|
You must also have a [supported editor (and their associated plugin if needed)](#editor-support) installed:
|
||||||
|
|
||||||
* `helix`
|
* `helix`
|
||||||
|
* `jed`
|
||||||
* `kakoune`
|
* `kakoune`
|
||||||
* `micro`
|
* `micro`
|
||||||
* `nano`
|
* `nano`
|
||||||
@@ -54,6 +58,26 @@ You must also have a [supported editor (and their associated plugin if needed)](
|
|||||||
|
|
||||||
## How to install
|
## How to install
|
||||||
|
|
||||||
|
### Nix flakes
|
||||||
|
|
||||||
|
Add this to your inputs in flake.nix:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
notewrapper = {
|
||||||
|
url = "github:tomasriveral/notewrapper";
|
||||||
|
};
|
||||||
|
```
|
||||||
|
If your flake already inputs nixpkgs and/or flake-utils, you can follow it:
|
||||||
|
```nix
|
||||||
|
notewrapper = {
|
||||||
|
url = "github:tomasriveral/notewrapper;
|
||||||
|
inputs.nixpkgs.follow = "nixpkgs";
|
||||||
|
inputs.flake-utils.follow = "flake-utils";
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building from source
|
||||||
|
|
||||||
1. Install all required dependencies listed above.
|
1. Install all required dependencies listed above.
|
||||||
2. Ensure you have a supported editor installed.
|
2. Ensure you have a supported editor installed.
|
||||||
3. Clone the repository:
|
3. Clone the repository:
|
||||||
@@ -77,7 +101,7 @@ nix-build
|
|||||||
make
|
make
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Configure `./config.json` (it is automatically created on first launch if it does not already exist).
|
5. [Configure](#configuration) `~/.config/notewrapper/config.json` (it is automatically created on first launch if it does not already exist).
|
||||||
6. Run the program:
|
6. Run the program:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
@@ -86,7 +110,7 @@ make
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Packaging
|
### Packaging
|
||||||
|
|
||||||
There is currently no official package available for NoteWrapper.
|
There is currently no official package available for NoteWrapper.
|
||||||
|
|
||||||
@@ -102,7 +126,6 @@ As a NixOS user, I will likely package it for **nixpkgs** in the future if the p
|
|||||||
Usage: notewrapper [options]
|
Usage: notewrapper [options]
|
||||||
Options:
|
Options:
|
||||||
-c, --config <path/to/config> Specify the config file.
|
-c, --config <path/to/config> Specify the config file.
|
||||||
-d, --directory <path/to/directory> Specify the vaults' directory.
|
|
||||||
-h, --help Display this message.
|
-h, --help Display this message.
|
||||||
-e, --editor Specify the editor to open.
|
-e, --editor Specify the editor to open.
|
||||||
-j, --jump Jump to the end of the file on opening.
|
-j, --jump Jump to the end of the file on opening.
|
||||||
@@ -135,23 +158,26 @@ NoteWrapper relies on certain editor features, so not all functionality is suppo
|
|||||||
|
|
||||||
### Features requiring editor support:
|
### Features requiring editor support:
|
||||||
|
|
||||||
* **Bufferless rendering**: updates the rendered view while typing (without saving)
|
* **Bufferless rendering**: updates the rendered view while typing (without saving). You won't need to save the file to see it updated in your browser.
|
||||||
* **Cursor following**: rendered view follows the cursor position
|
* **Cursor following**: rendered view follows the cursor position. When you scroll in your editor, it will automatically scroll your browser view.
|
||||||
* **Jump to end on open**: automatically moves the cursor to the end of the file
|
* **Jump to end on open**: automatically moves the cursor to the end of the file when oppening.
|
||||||
|
|
||||||
The first two features depend on [Vivify's editor integration](https://github.com/jannis-baum/Vivify?tab=readme-ov-file#existing-integration) and are mainly useful if you want external Markdown rendering in your browser.
|
The first two features depend on [Vivify's editor integration](https://github.com/jannis-baum/Vivify?tab=readme-ov-file#existing-integration)[^1] and are mainly useful if you want external Markdown rendering in your browser.
|
||||||
|
|
||||||
|
[^1]: Eventhough the features are Vivify-related, please report bugs and ask questions in this repo.
|
||||||
|
|
||||||
If your editor does not support these features, you can implement a plugin using [Vivify's API](https://github.com/jannis-baum/Vivify?tab=readme-ov-file#editor-support).
|
If your editor does not support these features, you can implement a plugin using [Vivify's API](https://github.com/jannis-baum/Vivify?tab=readme-ov-file#editor-support).
|
||||||
|
|
||||||
| Editor | Bufferless | Cursor | Jump to end | Aditional requirements |
|
| Editor | Bufferless | Cursor | Jump to end | Aditional requirements |
|
||||||
| --------- | ----------- | ------- | ------------ | ---------------------------------------------------------|
|
| --------- | ----------- | ------- | ------------ | ---------------------------------------------------------|
|
||||||
| Helix | ❌ | ❌ | ✅ | — |
|
| [Helix](https://helix-editor.com/) | ❌ | ❌ | ✅ | — |
|
||||||
| Kakoune | ❌ | ❌ | ✅ | — |
|
| [Jed](https://www.jedsoft.org/jed/) | ❌ | ❌ | ✅ | — |
|
||||||
| Micro | ❌ | ✅ | ✅ | [micro-vivify](https://codeberg.org/gibbert/micro-vivify) and [modifications to your `init.lua`](./docs/micro.md)|
|
| [Kakoune](http://kakoune.org/) | ❌ | ❌ | ✅ | — |
|
||||||
| Nano | ❌ | ❌ | ✅ | — |
|
| [Micro](https://micro-editor.github.io/) | ❌ | ✅ | ✅ | [micro-vivify](https://codeberg.org/gibbert/micro-vivify) and [modifications to your `init.lua`](./docs/micro.md)|
|
||||||
| Neovim | ✅ | ✅ | ✅ | [vivify-vim](https://github.com/jannis-baum/vivify.vim) |
|
| [Nano](https://www.nano-editor.org/) | ❌ | ❌ | ✅ | — |
|
||||||
| Vi | ❌ | ❌ | ✅ | |
|
| [Neovim](https://neovim.io/) | ✅ | ✅ | ✅ | [vivify-vim](https://github.com/jannis-baum/vivify.vim) |
|
||||||
| Vim | ✅ | ✅ | ✅ | [vivify-vim](https://github.com/jannis-baum/vivify.vim) |
|
| [Vi](https://en.wikipedia.org/wiki/Vi_(text_editor)) | ❌ | ❌ | ✅ | |
|
||||||
|
| [Vim](https://www.vim.org/) | ✅ | ✅ | ✅ | [vivify-vim](https://github.com/jannis-baum/vivify.vim) |
|
||||||
|
|
||||||
[How to add support for another editor](./CONTRIBUTING.md#adding-editor-support)
|
[How to add support for another editor](./CONTRIBUTING.md#adding-editor-support)
|
||||||
|
|
||||||
@@ -163,7 +189,7 @@ Edit `~/.config/notewrapper/config.json`. If it does not exist, it will be creat
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"directory": "~/Documents/Notes/",
|
"directory": ["~/Documents/Notes/", "/other/paths/"],
|
||||||
"render": true,
|
"render": true,
|
||||||
"jumpToEndOfFileOnLaunch": true,
|
"jumpToEndOfFileOnLaunch": true,
|
||||||
"editor": "neovim",
|
"editor": "neovim",
|
||||||
@@ -172,26 +198,48 @@ Edit `~/.config/notewrapper/config.json`. If it does not exist, it will be creat
|
|||||||
"newLineOnOpening": true,
|
"newLineOnOpening": true,
|
||||||
"backup": {
|
"backup": {
|
||||||
"enable": false,
|
"enable": false,
|
||||||
"directory": "/path/to/backup",
|
"directory": {
|
||||||
|
"~/Documents/Notes": "path/to/backup1",
|
||||||
|
"/other/paths/": "path/to/backup2"
|
||||||
|
},
|
||||||
"interval": "weekly",
|
"interval": "weekly",
|
||||||
"rsyncArgs": ["-Lqah", "--update"]
|
"rsyncArgs": ["-Lqah", "--update"],
|
||||||
|
"git": {
|
||||||
|
"enable": false,
|
||||||
|
"name": "Example Example",
|
||||||
|
"email": "mail@example.com"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Fields
|
### Fields
|
||||||
|
|
||||||
* `directory`: root directory containing all vaults
|
* `directory`: Array of directories containing the vaults.
|
||||||
* `render`: enable/disable Vivify rendering
|
* `render`: enable/disable Vivify rendering
|
||||||
* `jumpToEndOfFileOnLaunch`: move cursor to end of file on open
|
* `jumpToEndOfFileOnLaunch`: move cursor to end of file on open
|
||||||
* `editor`: selected editor (must be supported)
|
* `editor`: selected editor (must be supported). If not set, it defaults to `$EDITOR`.
|
||||||
* `journalRegex`: regex used to detect journal files
|
* `journalRegex`: regex used to detect journal files
|
||||||
* `dateEntry`: format for journal entries (see `strftime`)
|
* `dateEntry`: format for journal entries (see `strftime`)
|
||||||
* `newLineOnOpening`: add a newline when opening a note
|
* `newLineOnOpening`: add a newline when opening a note
|
||||||
* `backup.enable`: enable automatic backups using `rsync`
|
* `backup.enable`: enable automatic backups using `rsync`
|
||||||
* `backup.directory`: destination directory for backups
|
* `backup.directory`: backup's destination for each directory
|
||||||
* `backup.interval`: backup frequency (`daily`, `weekly`, `monthly`, or integer)
|
* `backup.interval`: backup frequency (`daily`, `weekly`, `monthly`, or integer)
|
||||||
* `backup.rsyncArgs`: arguments passed to `rsync`
|
* `backup.rsyncArgs`: arguments passed to `rsync`
|
||||||
|
* `git.enable`: enable git version control for backups
|
||||||
|
* `git.name` and `git.email` are for the commits signature
|
||||||
|
|
||||||
|
Note:
|
||||||
|
* Directories must end with `/`.
|
||||||
|
* All directories used must exist.
|
||||||
|
* Elements of `directory` and `backup.directory` must match exactly.
|
||||||
|
* Directories set in `directory` but not in `backup.directory` won't be backed up.
|
||||||
|
* Directories set in `backup.directory` but not in `directory` will be ignored.
|
||||||
|
* `journalRegex` must match if the file name ends with `.md` and if it doesn't.
|
||||||
|
* It is recommended to keep `-q` or `--quiet` flag in `rsyncArgs` to avoid interference with `ncurses`.
|
||||||
|
* If `rsync` fails, you will see it inside `ncurses`.
|
||||||
|
* If you want the git backup but without rsync, you can leave `backup.directory` empty or map each directory to itself. When enabled, git will apply to all your vaults.
|
||||||
|
* Each vault gets its own git repository that will be created the first time you select the vault with the option enabled.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -207,6 +255,7 @@ It is recommended to use a browser different from your main one for rendering.
|
|||||||
## Planned features
|
## Planned features
|
||||||
|
|
||||||
* [ ] A converter between journal types
|
* [ ] A converter between journal types
|
||||||
* [ ] Support multiple vault directories
|
* [x] Support multiple vault directories
|
||||||
* [ ] Port NoteWrapper to other editors (non-exhaustive list of planned ports: `emacs -nw`, `jed`, `ad`, flow-control, `ee`, `amp`, `dte`, `cano`, `mle`, `zee`, `ptext`, `kibi`, `ox`, `ne`, `dit`, `zile`, `moe`, `joe`, `pico`, `vis`)
|
* [ ] Port NoteWrapper to other editors (non-exhaustive list of planned ports: `emacs -nw`, `ad`, flow-control, `ee`, `amp`, `dte`, `cano`, `mle`, `zee`, `ptext`, `kibi`, `ox`, `ne`, `dit`, `zile`, `moe`, `joe`, `pico`, `vis`)
|
||||||
* [ ] Default to $EDITOR
|
* [x] Default to $EDITOR
|
||||||
|
* [ ] Refactoring the source code
|
||||||
-14
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"directory": "~/Documents/Notes/",
|
|
||||||
"render": true,
|
|
||||||
"jumpToEndOfFileOnLaunch": true,
|
|
||||||
"editor": "neovim",
|
|
||||||
"journalRegex": ".*journal.*",
|
|
||||||
"dateEntry": "# %Y %m %d %a",
|
|
||||||
"newLineOnOpening": true,
|
|
||||||
"backup": {
|
|
||||||
"enable": false,
|
|
||||||
"directory": "/path/to/backup",
|
|
||||||
"interval": "weekly"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-35
@@ -1,35 +0,0 @@
|
|||||||
{ pkgs ? import <nixpkgs> {} }:
|
|
||||||
|
|
||||||
pkgs.stdenv.mkDerivation {
|
|
||||||
pname = "notewrapper";
|
|
||||||
version = "1.0";
|
|
||||||
|
|
||||||
src = ./.;
|
|
||||||
|
|
||||||
nativeBuildInputs = [
|
|
||||||
pkgs.pkg-config
|
|
||||||
];
|
|
||||||
|
|
||||||
buildInputs = [
|
|
||||||
pkgs.ncurses
|
|
||||||
pkgs.cjson
|
|
||||||
pkgs.fzf
|
|
||||||
pkgs.gnused
|
|
||||||
pkgs.ripgrep
|
|
||||||
];
|
|
||||||
|
|
||||||
buildPhase = ''
|
|
||||||
make
|
|
||||||
'';
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
mkdir -p $out/bin
|
|
||||||
cp notewrapper $out/bin/
|
|
||||||
'';
|
|
||||||
|
|
||||||
meta = with pkgs.lib; {
|
|
||||||
description = " Journaling and notetaking TUI wrapper on top of neovim, vim and nano.";
|
|
||||||
license = licenses.gpl3Only;
|
|
||||||
platforms = platforms.linux;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Generated
+61
@@ -0,0 +1,61 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1786106723,
|
||||||
|
"narHash": "sha256-zDSUbpoeo/9ZmD2+wXnzxoo1+uhL8vxc0b8yuYMKYq0=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "f13ff45afd1bb73e640eaa08a7066dbed07e3238",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
{
|
||||||
|
description = "NoteWrapper";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, flake-utils}:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
in {
|
||||||
|
packages.default = pkgs.stdenv.mkDerivation (finalAttrs: {
|
||||||
|
pname = "notewrapper";
|
||||||
|
version = "v2.3.0";
|
||||||
|
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
nativeBuildInputs = with pkgs; [
|
||||||
|
pkg-config
|
||||||
|
gnumake
|
||||||
|
];
|
||||||
|
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
ncurses
|
||||||
|
cjson
|
||||||
|
vivify
|
||||||
|
rsync
|
||||||
|
ripgrep
|
||||||
|
fzf
|
||||||
|
gnused
|
||||||
|
libgit2
|
||||||
|
];
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
make VERSION=${finalAttrs.version}
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
cp notewrapper $out/bin/
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
cjson
|
||||||
|
ncurses
|
||||||
|
pkg-config
|
||||||
|
gdb
|
||||||
|
valgrind
|
||||||
|
clang-tools
|
||||||
|
gnumake
|
||||||
|
libgit2
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
apps.default = {
|
||||||
|
type = "app";
|
||||||
|
program = "${self.packages.${system}.default}/bin/notewrapper";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
CC := gcc
|
CC := gcc
|
||||||
|
|
||||||
CFLAGS := -Wall -Wextra -O2 \
|
VERSION := $(shell git describe --tags --always --dirty)
|
||||||
$(shell pkg-config --cflags libcjson ncurses)
|
|
||||||
|
|
||||||
LDFLAGS := $(shell pkg-config --libs libcjson ncurses)
|
CFLAGS := -Wall -Wextra -Werror -O2 \
|
||||||
|
-DVERSION=\"$(VERSION)\" \
|
||||||
|
$(shell pkg-config --cflags libcjson ncurses libgit2)
|
||||||
|
|
||||||
|
LDFLAGS := $(shell pkg-config --libs libcjson ncurses libgit2)
|
||||||
|
|
||||||
SRC := src/main.c src/ui.c src/utils.c src/notes.c
|
SRC := src/main.c src/ui.c src/utils.c src/notes.c
|
||||||
TARGET := notewrapper
|
TARGET := notewrapper
|
||||||
@@ -20,3 +23,17 @@ run: $(TARGET)
|
|||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(TARGET)
|
rm -f $(TARGET)
|
||||||
|
|
||||||
|
test: $(TARGET)
|
||||||
|
@timeout 5s ./$(TARGET); \
|
||||||
|
status=$$?; \
|
||||||
|
if [ $$status -eq 124 ]; then \
|
||||||
|
echo "Program ran for 5 seconds without crashing ✅"; \
|
||||||
|
exit 0; \
|
||||||
|
elif [ $$status -ne 0 ]; then \
|
||||||
|
echo "Program crashed ❌"; \
|
||||||
|
exit 1; \
|
||||||
|
else \
|
||||||
|
echo "Program exited cleanly ✅"; \
|
||||||
|
exit 0; \
|
||||||
|
fi
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{ pkgs ? import <nixpkgs> {} }:
|
|
||||||
|
|
||||||
pkgs.mkShell {
|
|
||||||
buildInputs = [
|
|
||||||
pkgs.cjson
|
|
||||||
pkgs.ncurses
|
|
||||||
pkgs.pkg-config
|
|
||||||
pkgs.gdb
|
|
||||||
pkgs.valgrind
|
|
||||||
pkgs.kdePackages.kcachegrind
|
|
||||||
];
|
|
||||||
}
|
|
||||||
+617
-407
File diff suppressed because it is too large.
Load diff
+267
-183
@@ -1,17 +1,45 @@
|
|||||||
#include "notes.h"
|
#include "notes.h"
|
||||||
#include "ui.h"
|
#include "utils.h"
|
||||||
|
|
||||||
char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug) {
|
char *getDirectoryFromVault(char *targetVault, char **vaultsArray, int vaultTotalNumber, int *vaultNumberPerDirectory, char **directoryArray, int directoryNumber, int shouldDebug) {
|
||||||
|
debug("Searching the vault %s inside all the directories...", targetVault);
|
||||||
|
debug("Here are how many vaults there is per directory:");
|
||||||
|
for (int i = 0; i < directoryNumber; i++) {
|
||||||
|
altDebug("%d for %s\n", vaultNumberPerDirectory[i], directoryArray[i]);
|
||||||
|
}
|
||||||
|
debug("Here are all the vaults in the order they will be searched:");
|
||||||
|
for (int i = 0; i < vaultTotalNumber; i++) {
|
||||||
|
altDebug("%s\n", vaultsArray[i]);
|
||||||
|
}
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < directoryNumber; i++) {
|
||||||
|
for (int j = 0; j < vaultNumberPerDirectory[i]; j++) {
|
||||||
|
if (strcmp(vaultsArray[index], targetVault) == 0) {
|
||||||
|
debug("%s found in %s. (index %d)", targetVault, directoryArray[i], index);
|
||||||
|
return directoryArray[i];
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error(1, "program", "the vault %s was not found", targetVault);
|
||||||
|
return "this makes the compiler and clangd happy. Who doesn't want GCC and clangd to be happy? "
|
||||||
|
"Such person would be a terrible monster... One must imagine GCC and clangd happy.";
|
||||||
|
}
|
||||||
|
|
||||||
|
char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug) {
|
||||||
debug("Searching %s for journals", vault);
|
debug("Searching %s for journals", vault);
|
||||||
// originally from https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
// originally from
|
||||||
|
// https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
||||||
struct dirent *vaultEntry;
|
struct dirent *vaultEntry;
|
||||||
char tempPath[PATH_MAX];
|
char tempPath[PATH_MAX];
|
||||||
snprintf(tempPath, sizeof(tempPath), "%s/%s", pathToVault, vault); // sets the full absolute path to fullPathEntry
|
snprintf(tempPath, sizeof(tempPath), "%s/%s", pathToVault,
|
||||||
|
vault); // sets the full absolute path to fullPathEntry
|
||||||
DIR *vaultDirectory = opendir(tempPath);
|
DIR *vaultDirectory = opendir(tempPath);
|
||||||
error(vaultDirectory==NULL, "program", "Could not open directory %s", tempPath);
|
error(vaultDirectory == NULL, "program", "Could not open directory %s", tempPath);
|
||||||
char **journalsArray = NULL; // will contain all the notes
|
char **journalsArray = NULL; // will contain all the notes
|
||||||
int journalsCount = 0; // we need to count how many notes there is to always readjust how many memory we alloc
|
int journalsCount = 0; // we need to count how many notes there is to always readjust how many memory we alloc
|
||||||
|
|
||||||
// https://stackoverflow.com/a/1085120 for regex code
|
// https://stackoverflow.com/a/1085120 for regex code
|
||||||
regex_t regex;
|
regex_t regex;
|
||||||
int regexReturn;
|
int regexReturn;
|
||||||
@@ -23,20 +51,21 @@ char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex,
|
|||||||
// for readdir()
|
// for readdir()
|
||||||
debug("┌------------------------------------------\nDetected files and dirs from the vault");
|
debug("┌------------------------------------------\nDetected files and dirs from the vault");
|
||||||
while ((vaultEntry = readdir(vaultDirectory)) != NULL) { // we iterate over every entry from the dir. So files and dirs (. and .. included)
|
while ((vaultEntry = readdir(vaultDirectory)) != NULL) { // we iterate over every entry from the dir. So files and dirs (. and .. included)
|
||||||
altDebug("%s ", vaultEntry->d_name);
|
altDebug("%s ", vaultEntry->d_name);
|
||||||
if (vaultEntry->d_name[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
if (vaultEntry->d_name[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
||||||
regexReturn = regexec(®ex, vaultEntry->d_name, 0, NULL, 0);
|
regexReturn = regexec(®ex, vaultEntry->d_name, 0, NULL, 0);
|
||||||
if (!regexReturn) { // if the regex matches
|
if (!regexReturn) { // if the regex matches
|
||||||
altDebug("matched with the regex. It is a journal.\n");
|
altDebug("matched with the regex. It is a journal.\n");
|
||||||
journalsArray = realloc(journalsArray, (journalsCount + 1)*sizeof(char*)); // resize notesArray so that
|
journalsArray = realloc(journalsArray,
|
||||||
journalsArray[journalsCount] = strdup(vaultEntry->d_name); // copy the dir name into notesArray
|
(journalsCount + 1) * sizeof(char *)); // resize notesArray so that
|
||||||
journalsCount++;
|
journalsArray[journalsCount] = strdup(vaultEntry->d_name); // copy the dir name into notesArray
|
||||||
|
journalsCount++;
|
||||||
|
} else {
|
||||||
|
altDebug("did not matched with the regex. It is a note.\n");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
altDebug("did not matched with the regex. It is a note.\n");
|
altDebug(" was ignored\n");
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
altDebug(" was ignored\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
altDebug("└ ------------------------------\n");
|
altDebug("└ ------------------------------\n");
|
||||||
// free's some used memory
|
// free's some used memory
|
||||||
@@ -45,42 +74,51 @@ char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex,
|
|||||||
return journalsArray;
|
return journalsArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
char** getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug) {
|
char **getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug) {
|
||||||
// this function is inputed a path to a vault (which was selected before) and outpus all the suitable notes (so not the hidden ones)
|
// this function is inputed a path to a vault (which was selected before) and outpus all the
|
||||||
// originally from https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
// suitable notes (so not the hidden ones) originally from
|
||||||
|
// https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
||||||
debug("Searching %s for notes", vault);
|
debug("Searching %s for notes", vault);
|
||||||
struct dirent *vaultEntry;
|
struct dirent *vaultEntry;
|
||||||
char tempPath[PATH_MAX];
|
char tempPath[PATH_MAX];
|
||||||
snprintf(tempPath, sizeof(tempPath), "%s/%s", pathToVault, vault); // sets the full absolute path to fullPathEntry
|
snprintf(tempPath, sizeof(tempPath), "%s/%s", pathToVault,
|
||||||
|
vault); // sets the full absolute path to fullPathEntry
|
||||||
DIR *vaultDirectory = opendir(tempPath);
|
DIR *vaultDirectory = opendir(tempPath);
|
||||||
error(vaultDirectory==NULL, "program", "Could not open directory %s", tempPath);
|
error(vaultDirectory == NULL, "program", "Could not open directory %s", tempPath);
|
||||||
char **notesArray = NULL; // will contain all the notes
|
char **notesArray = NULL; // will contain all the notes
|
||||||
int notesCount = 0; // we need to count how many notes there is to always readjust how many memory we alloc
|
int notesCount = 0; // we need to count how many notes there is to always readjust how many memory we alloc
|
||||||
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
||||||
// for readdir()
|
// for readdir()
|
||||||
debug("┌------------------------------\nDetected Files and dirs from the vault:");
|
debug("┌------------------------------\nDetected Files and dirs from the vault:");
|
||||||
while ((vaultEntry = readdir(vaultDirectory)) != NULL) {
|
while ((vaultEntry = readdir(vaultDirectory)) != NULL) {
|
||||||
char *entryName = vaultEntry->d_name;
|
char *entryName = vaultEntry->d_name;
|
||||||
int entryLenght = strlen(entryName);
|
int entryLenght = strlen(entryName);
|
||||||
altDebug("%s ", entryName);
|
altDebug("%s ", entryName);
|
||||||
// for the regex code https://stackoverflow.com/a/1085120
|
// for the regex code https://stackoverflow.com/a/1085120
|
||||||
regex_t regex;
|
regex_t regex;
|
||||||
int regexReturn;
|
int regexReturn;
|
||||||
// compiles the regex
|
// compiles the regex
|
||||||
regexReturn = regcomp(®ex, journalRegex, 0);
|
regexReturn = regcomp(®ex, journalRegex, 0);
|
||||||
if (entryName[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
if (entryName[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
||||||
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
||||||
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s/%s", pathToVault, vault, entryName); // sets the full absolute path to fullPathEntry
|
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s/%s", pathToVault, vault,
|
||||||
// check if it matches the regex. If it does not match regexReturn != 0.
|
entryName); // sets the full absolute path to fullPathEntry
|
||||||
regexReturn = regexec(®ex, entryName, 0, NULL, 0);
|
// check if it matches the regex. If it does not match regexReturn != 0.
|
||||||
struct stat metadataPathEntry;
|
regexReturn = regexec(®ex, entryName, 0, NULL, 0);
|
||||||
if (stat(fullPathEntry, &metadataPathEntry) == 0 && entryName[entryLenght - 3] == '.' && entryName[entryLenght - 2] == 'm' && entryName[entryLenght - 1] == 'd' && regexReturn && S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a file ending in .md and that does no match the regex
|
struct stat metadataPathEntry;
|
||||||
notesArray = realloc(notesArray, (notesCount + 1)*sizeof(char*)); // resize notesArray so that
|
if (stat(fullPathEntry, &metadataPathEntry) == 0 && entryName[entryLenght - 3] == '.' && entryName[entryLenght - 2] == 'm' && entryName[entryLenght - 1] == 'd' && regexReturn &&
|
||||||
notesArray[notesCount] = strdup(entryName); // copy the dir name into notesArray
|
S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a file ending in .md and
|
||||||
notesCount++;
|
// that does no match the regex
|
||||||
altDebug("did not match with the regex. It is a note.\n");
|
notesArray = realloc(notesArray, (notesCount + 1) * sizeof(char *)); // resize notesArray so that
|
||||||
} else if (!regexReturn) {altDebug("matched with the regex. It is a journal.\n");}
|
notesArray[notesCount] = strdup(entryName); // copy the dir name into notesArray
|
||||||
} else {altDebug("was ignored\n");}
|
notesCount++;
|
||||||
|
altDebug("did not match with the regex. It is a note.\n");
|
||||||
|
} else if (!regexReturn) {
|
||||||
|
altDebug("matched with the regex. It is a journal.\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
altDebug("was ignored\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
altDebug("└ ------------------------------\n");
|
altDebug("└ ------------------------------\n");
|
||||||
|
|
||||||
@@ -90,148 +128,194 @@ char** getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int
|
|||||||
return notesArray;
|
return notesArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
char **getVaultsFromDirectory(char *dirString, int *count, int shouldDebug) {
|
char **getVaultsFromDirectories(char **directoryStringArray, int directoryNumber, int *vaultsPerDirectoryNumber, int *count, int shouldDebug) {
|
||||||
// this function is inputed a path to a directory (which comes usually from the config file) and outpus all the suitable directories (so not the hidden ones) which will serve as separate vaults for notes
|
// to avoid having to work with a tree-dimensional array. We will use a 2d and
|
||||||
debug("Opening %s ", dirString);
|
// vaultsPerDirectoryNumber will indicate the width of the directories.
|
||||||
// originally from https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
char **vaultsArray = NULL;
|
||||||
struct dirent *vaultsDirectoryEntry;
|
int nthVault = 0; // this is only used internally to set the string into the right place in
|
||||||
DIR *vaultsDirectory = opendir(dirString);
|
// directoryStringArray.
|
||||||
error(vaultsDirectory==NULL, "program", "Could not open directory %s", dirString);
|
int previousStartIndex = 0;
|
||||||
char **dirsArray = NULL; // will contain all the dirs/vaults
|
for (int i = 0; i < directoryNumber; i++) {
|
||||||
int dirsCount = 0; // we need to count how many dirs there is to always readjust how many memory we alloc
|
debug("Opening %s", directoryStringArray[i]);
|
||||||
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
// originally from
|
||||||
// for readdir()
|
// https://www.geeksforgeeks.org/c/c-program-list-files-sub-directories-directory/
|
||||||
debug("┌------------------------------\n Detected files and dirs from the directory:");
|
struct dirent *vaultsDirectoryEntry;
|
||||||
while ((vaultsDirectoryEntry = readdir(vaultsDirectory)) != NULL) {
|
DIR *vaultsDirectory = opendir(directoryStringArray[i]);
|
||||||
altDebug("%s\n", vaultsDirectoryEntry->d_name);
|
error(!vaultsDirectory, "program", "Could not open directory %s", directoryStringArray[i]);
|
||||||
if (vaultsDirectoryEntry->d_name[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
vaultsPerDirectoryNumber[i] = 0;
|
||||||
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
debug("┌------------------------------\n Detected files and dirs %s:", directoryStringArray[i]);
|
||||||
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s", dirString, vaultsDirectoryEntry->d_name); // sets the full absolute path to fullPathEntry
|
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
||||||
|
// for readdir()
|
||||||
struct stat metadataPathEntry;
|
while ((vaultsDirectoryEntry = readdir(vaultsDirectory)) != NULL) {
|
||||||
if (stat(fullPathEntry, &metadataPathEntry) == 0 && S_ISDIR(metadataPathEntry.st_mode)) { // if this entry is a directory
|
char *entryName = vaultsDirectoryEntry->d_name; // gets the entry as a string value
|
||||||
dirsArray = realloc(dirsArray, (dirsCount + 1)*sizeof(char*)); // resize dirsArray so that
|
altDebug("%s", entryName);
|
||||||
dirsArray[dirsCount] = strdup(vaultsDirectoryEntry->d_name); // copy the dir name into dirsArray
|
if (entryName[0] != '.') { // if not hidden file/dir
|
||||||
dirsCount++;
|
char tempFullEntryPath[PATH_MAX]; // we recreate the full path to check it's
|
||||||
|
// proprieties
|
||||||
|
snprintf(tempFullEntryPath, PATH_MAX, "%s%s", directoryStringArray[i], entryName);
|
||||||
|
altDebug(" (%s)", tempFullEntryPath);
|
||||||
|
// checking the metadata to see if it is a dir
|
||||||
|
struct stat metadataEntry;
|
||||||
|
if (stat(tempFullEntryPath, &metadataEntry) == 0 && S_ISDIR(metadataEntry.st_mode)) { // get's the metadata (stat should return 0 if
|
||||||
|
// fails) and sees if it is a dir.
|
||||||
|
altDebug(" is a vault");
|
||||||
|
vaultsArray = realloc(vaultsArray, sizeof(char *) * (nthVault + 1)); // resize vaultsArray
|
||||||
|
error(vaultsArray == NULL, "program", "realloc failed");
|
||||||
|
vaultsPerDirectoryNumber[i]++; // it will be used later to know which vaults
|
||||||
|
// goes into which directory
|
||||||
|
vaultsArray[nthVault] = strdup(entryName); // we use strdup and not strcpy, because memory used with
|
||||||
|
// opendir and readdir will be closed.
|
||||||
|
error(vaultsArray[nthVault] == NULL, "program", "strdup failed");
|
||||||
|
nthVault++; // it is used immediatly to set the vault into directoryStringArray
|
||||||
|
} else {
|
||||||
|
altDebug(" is not a vault (not a dir.)");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
altDebug(" is not a vault (hidden file/dir)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
// we sort entries for the same vault. We can't sort them all. This would break the order.
|
||||||
|
qsort(vaultsArray + previousStartIndex, nthVault - previousStartIndex, sizeof(const char *),
|
||||||
|
compareString); // sorts the vaults alphabetically
|
||||||
|
closedir(vaultsDirectory);
|
||||||
|
previousStartIndex = nthVault;
|
||||||
}
|
}
|
||||||
altDebug("└------------------------------\n");
|
// we calculate once the total number of vaults to avoid recalculation every time we use it
|
||||||
|
*count = 0;
|
||||||
// free's some used memory
|
for (int i = 0; i < directoryNumber; i++) {
|
||||||
closedir(vaultsDirectory);
|
*count += vaultsPerDirectoryNumber[i];
|
||||||
*count = dirsCount;
|
}
|
||||||
return dirsArray;
|
return vaultsArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWasUpdated, int shouldDebug) {
|
char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWasUpdated, int *returnNoteSelection, int shouldDebug) {
|
||||||
path[PATH_MAX] = '\0'; // it assures it is a string (Most cases this does nothing). But rewriting at least one bytes make the compile happy. He doesn't want to return an unchanged input.
|
path[PATH_MAX] = '\0'; // it assures it is a string (Most cases this does nothing). But rewriting at least
|
||||||
debug("Handling the journal %s", path);
|
// one bytes make the compile happy. He doesn't want to return an unchanged input.
|
||||||
|
debug("Handling the journal %s", path);
|
||||||
|
|
||||||
char *date = getFormatedTime(timeFormat, shouldDebug);
|
char *date = getFormatedTime(timeFormat, shouldDebug);
|
||||||
debug("Formated time for the journal's entry is %s", date);
|
debug("Formated time for the journal's entry is %s", date);
|
||||||
char dateWithExtension[PATH_MAX];
|
char dateWithExtension[PATH_MAX];
|
||||||
snprintf(dateWithExtension, PATH_MAX, "%s.md", date);
|
snprintf(dateWithExtension, PATH_MAX, "%s.md", date);
|
||||||
sanitize(dateWithExtension);
|
sanitize(dateWithExtension);
|
||||||
debug("Sanitized date: %s\n(it might be used later for a file name if the journal is divided)", dateWithExtension);
|
debug("Sanitized date: %s\n(it might be used later for a file name if the journal is divided)", dateWithExtension);
|
||||||
struct stat metadata;
|
struct stat metadata;
|
||||||
error(stat(path, &metadata), "program", "stat() failed to get information about %s", path);
|
stat(path, &metadata);
|
||||||
if (S_ISREG(metadata.st_mode)) {
|
if (S_ISREG(metadata.st_mode)) {
|
||||||
debug("%s is a unified journal.", path);
|
debug("%s is a unified journal.", path);
|
||||||
if (!isStringInFile(path, date, shouldDebug)) { // if there is no entry for current date
|
if (!isStringInFile(path, date, shouldDebug)) { // if there is no entry for current date
|
||||||
appendToFile(path, date, shouldDebug);
|
appendToFile(path, date, shouldDebug);
|
||||||
*journalWasUpdated = 1;
|
*journalWasUpdated = 1;
|
||||||
} // if there is an entry do nothing
|
} // if there is an entry do nothing
|
||||||
} else if (S_ISDIR(metadata.st_mode)) {
|
} else if (S_ISDIR(metadata.st_mode)) {
|
||||||
debug("%s is a divided journal.", path);
|
debug("%s is a divided journal.", path);
|
||||||
struct dirent *dividedJournalEntry;
|
struct dirent *dividedJournalEntry;
|
||||||
// snprintf does not like to have the same variable as input and output so we use a buffer
|
|
||||||
char temp[PATH_MAX];
|
|
||||||
snprintf(temp, PATH_MAX, "%s/", path); // appends a /. For safety
|
|
||||||
strncpy(path, temp, PATH_MAX);
|
|
||||||
DIR *dividedJournalDirectory = opendir(path);
|
|
||||||
error(dividedJournalDirectory==NULL, "program", "Could not open directory %s", path);
|
|
||||||
/*char **entryArray = NULL; // will contain all the entries of the divided journal
|
|
||||||
// This time it will be different. We must add "invert" the extraOptions to the options so that the Create new entry in journal is on top
|
|
||||||
entryArray = realloc(entryArray, sizeof(char*));*/
|
|
||||||
// simpler to just malloc 2 and later realloc
|
|
||||||
const int extraOptions = 3;
|
|
||||||
char **entryArray = malloc(extraOptions*sizeof(char*));
|
|
||||||
char *createEntryMessage = malloc(PATH_MAX);
|
|
||||||
snprintf(createEntryMessage, PATH_MAX, "Create new entry for the journal %s", journal);
|
|
||||||
entryArray[0] = createEntryMessage;
|
|
||||||
entryArray[1] = "Open random entry";
|
|
||||||
entryArray[2] = "Search inside entries";
|
|
||||||
int entryCount = extraOptions; // we need to count how many dirs there is to always readjust how many memory we alloc
|
|
||||||
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
|
||||||
// for readdir()
|
|
||||||
debug("┌------------------------------\n Detected files and dirs from %s:", path);
|
|
||||||
// iterates over all the entries from the dir
|
|
||||||
while ((dividedJournalEntry = readdir(dividedJournalDirectory)) != NULL) {
|
|
||||||
altDebug("%s\n", dividedJournalEntry->d_name);
|
|
||||||
if (dividedJournalEntry->d_name[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
|
||||||
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
|
||||||
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s", path, dividedJournalEntry->d_name); // sets the full absolute path to fullPathEntry
|
|
||||||
|
|
||||||
struct stat metadataPathEntry;
|
|
||||||
if (stat(fullPathEntry, &metadataPathEntry) == 0 && S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a directory
|
|
||||||
entryArray = realloc(entryArray, (entryCount + 1)*sizeof(char*));
|
|
||||||
entryArray[entryCount] = strdup(dividedJournalEntry->d_name); // copy the dir name into entryArray
|
|
||||||
entryCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
altDebug("└------------------------------\n");
|
|
||||||
// free's some used memory
|
|
||||||
closedir(dividedJournalDirectory);
|
|
||||||
qsort(entryArray + extraOptions, entryCount - extraOptions, sizeof(const char *), reverseCompareString); // sorts the journals entries alphabetically. // the + extraOptions - extraOptions is to not sort "Create new entry for the journal" or Open random entry which is the first element
|
|
||||||
|
|
||||||
// we must now select to create new entry or to enter in old one
|
|
||||||
char *selectedOption = ncursesSelect(entryArray, "Create new entry or acces old entry (Use arrows or WASD, Enter to select):", extraOptions, entryCount - extraOptions, " ", " ", "", shouldDebug); // the "Create new entry for the journal %s" will be the only options. All other will be extraOptions. This is made so that "Create [...] %s" will always be on top
|
|
||||||
debug("Selected option from journal entry selection: %s", selectedOption);
|
|
||||||
if (strcmp(selectedOption, createEntryMessage) == 0) { // create new entry
|
|
||||||
char temp[PATH_MAX];
|
|
||||||
error(strlen(path)+1+strlen(dateWithExtension)+1>PATH_MAX, "Error file path too long. %s/%s must not exceed PATH_MAX", path, dateWithExtension);
|
|
||||||
snprintf(temp, PATH_MAX, "%s/%s", path, dateWithExtension);
|
|
||||||
strncpy(path, temp, PATH_MAX);
|
|
||||||
if (!isStringInArray(dateWithExtension, (const char **)entryArray, entryCount)) { // it only creates it if it doesn't already exist
|
|
||||||
// if it does exist it will just pass the full path
|
|
||||||
debug("Creating entry %s inside %s", date, path);
|
|
||||||
FILE *file;
|
|
||||||
file = fopen(path, "w");
|
|
||||||
error(file == NULL, "program", "%s couldn't be created", path);
|
|
||||||
debug("Writing %s\\n to %s", date, path);
|
|
||||||
fprintf(file, "%s\n", date);
|
|
||||||
fclose(file);
|
|
||||||
free(createEntryMessage);
|
|
||||||
*journalWasUpdated = 1;
|
|
||||||
} else {
|
|
||||||
debug("Today's entry (%s) already exist. We won't create a new one.", dateWithExtension);
|
|
||||||
}
|
|
||||||
} else if (strcmp(selectedOption, entryArray[1]) == 0) { // if we choosed to open a random entry
|
|
||||||
// setting up the seed for rand()
|
|
||||||
srand(time(NULL)); // not totaly random, just pseudorandom
|
|
||||||
int randomEntry = extraOptions + (rand() % (entryCount - extraOptions)); // we get a random number between extraOptions and entryCount (so all journal entries and not extraOptions)
|
|
||||||
char temp[PATH_MAX];
|
|
||||||
snprintf(temp, PATH_MAX, "%s/%s", path, entryArray[randomEntry]); // reconstruct the path
|
|
||||||
strncpy(path, temp, PATH_MAX);
|
|
||||||
debug("Selected random entry %s. It's path is %s.", entryArray[randomEntry], path);
|
|
||||||
} else if (strcmp(selectedOption, entryArray[2]) == 0) { // if we choosed to search
|
|
||||||
char *temp = fzfSelect(path, "Input text to be searched", shouldDebug); // uses ripgrep and fzf to search inside the files
|
|
||||||
char *selectedOption = malloc(PATH_MAX); // we can't just use path as both input and output of snprintf
|
|
||||||
snprintf(selectedOption, PATH_MAX, "%s/%s", path, temp);
|
|
||||||
path = strdup(selectedOption);
|
|
||||||
free(selectedOption);
|
|
||||||
} else { // we just recreate the path to the selected entry
|
|
||||||
// snprintf does not like to have the same variable as input and output so we use a buffer
|
// snprintf does not like to have the same variable as input and output so we use a buffer
|
||||||
char temp[PATH_MAX];
|
char temp[PATH_MAX];
|
||||||
snprintf(temp, PATH_MAX, "%s/%s", path, selectedOption);
|
snprintf(temp, PATH_MAX, "%s/", path); // appends a /. For safety
|
||||||
strncpy(path, temp, PATH_MAX);
|
strncpy(path, temp, PATH_MAX);
|
||||||
debug("Returning path to the selected entry: %s", path);
|
DIR *dividedJournalDirectory = opendir(path);
|
||||||
}
|
error(dividedJournalDirectory == NULL, "program", "Could not open directory %s", path);
|
||||||
} else {
|
/*char **entryArray = NULL; // will contain all the entries of the divided journal
|
||||||
error(1, "program", "%s is not a file and not a directory.", path);
|
// This time it will be different. We must add "invert" the extraOptions to the options so
|
||||||
}
|
that the Create new entry in journal is on top entryArray = realloc(entryArray,
|
||||||
|
sizeof(char*));*/
|
||||||
return path;
|
// simpler to just malloc 2 and later realloc
|
||||||
|
const int extraOptions = 4;
|
||||||
|
char **entryArray = malloc(extraOptions * sizeof(char *));
|
||||||
|
char *createEntryMessage = malloc(PATH_MAX);
|
||||||
|
snprintf(createEntryMessage, PATH_MAX, "Create new entry for the journal %s", journal);
|
||||||
|
entryArray[0] = createEntryMessage;
|
||||||
|
entryArray[1] = "Open random entry";
|
||||||
|
entryArray[2] = "Search inside entries";
|
||||||
|
entryArray[3] = "Return to the note selection";
|
||||||
|
int entryCount = extraOptions; // we need to count how many dirs there is to always readjust
|
||||||
|
// how many memory we alloc
|
||||||
|
// Refer https://pubs.opengroup.org/onlinepubs/7990989775/xsh/readdir.html
|
||||||
|
// for readdir()
|
||||||
|
debug("┌------------------------------\n Detected files and dirs from %s:", path);
|
||||||
|
// iterates over all the entries from the dir
|
||||||
|
while ((dividedJournalEntry = readdir(dividedJournalDirectory)) != NULL) {
|
||||||
|
altDebug("%s\n", dividedJournalEntry->d_name);
|
||||||
|
if (dividedJournalEntry->d_name[0] != '.') { // if the entry don't start with a dot (so hidden dirs and hidden files)
|
||||||
|
char fullPathEntry[PATH_MAX]; // creates a string of size of the maximum path lenght
|
||||||
|
snprintf(fullPathEntry, sizeof(fullPathEntry), "%s/%s", path,
|
||||||
|
dividedJournalEntry->d_name); // sets the full absolute path to fullPathEntry
|
||||||
|
|
||||||
|
struct stat metadataPathEntry;
|
||||||
|
if (stat(fullPathEntry, &metadataPathEntry) == 0 && S_ISREG(metadataPathEntry.st_mode)) { // if this entry is a directory
|
||||||
|
entryArray = realloc(entryArray, (entryCount + 1) * sizeof(char *));
|
||||||
|
entryArray[entryCount] = strdup(dividedJournalEntry->d_name); // copy the dir name into entryArray
|
||||||
|
entryCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
altDebug("└------------------------------\n");
|
||||||
|
// free's some used memory
|
||||||
|
closedir(dividedJournalDirectory);
|
||||||
|
qsort(entryArray + extraOptions, entryCount - extraOptions, sizeof(const char *),
|
||||||
|
reverseCompareString); // sorts the journals entries alphabetically. // the +
|
||||||
|
// extraOptions - extraOptions is to not sort "Create new entry
|
||||||
|
// for the journal" or Open random entry which is the first
|
||||||
|
// element
|
||||||
|
|
||||||
|
// we must now select to create new entry or to enter in old one
|
||||||
|
char *selectedOption =
|
||||||
|
ncursesSelect(entryArray, "Create new entry or acces old entry (Use arrows or WASD, Enter to select or use 1-9 to quick select):", extraOptions, entryCount - extraOptions, " ", " ", "",
|
||||||
|
shouldDebug); // the "Create new entry for the journal %s" will be the only options. All
|
||||||
|
// other will be extraOptions. This is made so that "Create [...] %s" will
|
||||||
|
// always be on top
|
||||||
|
debug("Selected option from journal entry selection: %s", selectedOption);
|
||||||
|
if (strcmp(selectedOption, createEntryMessage) == 0) { // create new entry
|
||||||
|
char temp[PATH_MAX];
|
||||||
|
error(strlen(path) + 1 + strlen(dateWithExtension) + 1 > PATH_MAX, "Error file path too long. %s/%s must not exceed PATH_MAX", path, dateWithExtension);
|
||||||
|
snprintf(temp, PATH_MAX, "%s/%s", path, dateWithExtension);
|
||||||
|
strncpy(path, temp, PATH_MAX);
|
||||||
|
if (!isStringInArray(dateWithExtension, (const char **)entryArray,
|
||||||
|
entryCount)) { // it only creates it if it doesn't already exist
|
||||||
|
// if it does exist it will just pass the full path
|
||||||
|
debug("Creating entry %s inside %s", date, path);
|
||||||
|
FILE *file;
|
||||||
|
file = fopen(path, "w");
|
||||||
|
error(file == NULL, "program", "%s couldn't be created", path);
|
||||||
|
debug("Writing %s\\n to %s", date, path);
|
||||||
|
fprintf(file, "%s\n", date);
|
||||||
|
fclose(file);
|
||||||
|
free(createEntryMessage);
|
||||||
|
*journalWasUpdated = 1;
|
||||||
|
} else {
|
||||||
|
debug("Today's entry (%s) already exist. We won't create a new one.", dateWithExtension);
|
||||||
|
}
|
||||||
|
} else if (strcmp(selectedOption, entryArray[1]) == 0) { // if we choosed to open a random entry
|
||||||
|
// setting up the seed for rand()
|
||||||
|
srand(time(NULL)); // not totaly random, just pseudorandom
|
||||||
|
int randomEntry = extraOptions + (rand() % (entryCount - extraOptions)); // we get a random number between extraOptions and entryCount (so
|
||||||
|
// all journal entries and not extraOptions)
|
||||||
|
char temp[PATH_MAX];
|
||||||
|
snprintf(temp, PATH_MAX, "%s/%s", path,
|
||||||
|
entryArray[randomEntry]); // reconstruct the path
|
||||||
|
strncpy(path, temp, PATH_MAX);
|
||||||
|
debug("Selected random entry %s. It's path is %s.", entryArray[randomEntry], path);
|
||||||
|
} else if (strcmp(selectedOption, entryArray[2]) == 0) { // if we choosed to search
|
||||||
|
char *temp = fzfSelect(path, "Input text to be searched",
|
||||||
|
shouldDebug); // uses ripgrep and fzf to search inside the files
|
||||||
|
char *selectedOption = malloc(PATH_MAX); // we can't just use path as both input and output of snprintf
|
||||||
|
snprintf(selectedOption, PATH_MAX, "%s/%s", path, temp);
|
||||||
|
path = strdup(selectedOption);
|
||||||
|
free(selectedOption);
|
||||||
|
} else if (strcmp(selectedOption, "Return to the note selection") == 0) {
|
||||||
|
*returnNoteSelection = 1;
|
||||||
|
} else { // we just recreate the path to the selected entry
|
||||||
|
// snprintf does not like to have the same variable as input and output so we use a
|
||||||
|
// buffer
|
||||||
|
char temp[PATH_MAX];
|
||||||
|
snprintf(temp, PATH_MAX, "%s/%s", path, selectedOption);
|
||||||
|
strncpy(path, temp, PATH_MAX);
|
||||||
|
debug("Returning path to the selected entry: %s", path);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error(1, "program", "%s is not a file and not a directory.", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
+17
-8
@@ -1,22 +1,31 @@
|
|||||||
#ifndef NOTES_H
|
#ifndef NOTES_H
|
||||||
#define NOTES_H
|
#define NOTES_H
|
||||||
#include "utils.h"
|
|
||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
|
#include "utils.h"
|
||||||
|
// find which directory contains targetVault
|
||||||
|
char *getDirectoryFromVault(char *targetVault, char **vaultsArray, int vaultTotalNumber, int *vaultNumberPerDirectory, char **directoryArray, int directoryNumber, int shouldDebug);
|
||||||
// gets the journals from the vault.
|
// gets the journals from the vault.
|
||||||
//to distinguish journals from note we use regex.
|
// to distinguish journals from note we use regex.
|
||||||
//Note: we must handle them separatly as journals can either be a single file (which will treat specially) or a directory.
|
// Note: we must handle them separatly as journals can either be a single file (which will treat
|
||||||
|
// specially) or a directory.
|
||||||
char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug);
|
char **getJournalsFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug);
|
||||||
// this function is inputed a path to a vault (which was selected before) and outpus all the suitable notes (so not the hidden ones).
|
// this function is inputed a path to a vault (which was selected before) and outpus all the
|
||||||
// journalRegex is the regex code for the journals. If a note matches this code, it is treated as a journal and it is not outputed from this function.
|
// suitable notes (so not the hidden ones). journalRegex is the regex code for the journals. If a
|
||||||
|
// note matches this code, it is treated as a journal and it is not outputed from this function.
|
||||||
char **getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug);
|
char **getNotesFromVault(char *pathToVault, char *vault, char *journalRegex, int *count, int shouldDebug);
|
||||||
// this function is inputed a path to a directory (which comes usually from the config file) and outpus all the suitable directories (so not the hidden ones) which will serve as separate vaults for notes.
|
// function gets as input an array of directoryNumber strings. Which are the directories the
|
||||||
char **getVaultsFromDirectory(char *dirString, int *count, int shouldDebug);
|
// function will search for vaults. it returns an array of vaults. the vaults are organized in order
|
||||||
|
// per directory. vaultsPerDirectoryNumber and count should be initiallized before calling the
|
||||||
|
// function and the address be inputed. count is the total number of vaults.
|
||||||
|
// vaultsPerDirectoryNumber gives how many vaults each directory has.
|
||||||
|
char **getVaultsFromDirectories(char **directoryStringArray, int directoryNumber, int *vaultsPerDirectoryNumber, int *count, int shouldDebug);
|
||||||
// path is the path to the file.
|
// path is the path to the file.
|
||||||
// journal is the name of the file.
|
// journal is the name of the file.
|
||||||
// journalWasUpdated will be set to 1 if a new entry was created
|
// journalWasUpdated will be set to 1 if a new entry was created
|
||||||
// handles both type of journal (divided and unified).
|
// handles both type of journal (divided and unified).
|
||||||
// creates new entry with date.
|
// creates new entry with date.
|
||||||
// for divided select if we want to acces to a new entry or a old one.
|
// for divided select if we want to acces to a new entry or a old one.
|
||||||
|
// returnNoteSelection tells us if the user wants to go back to the note selection.
|
||||||
// returns the path to the file that needs to be opened.
|
// returns the path to the file that needs to be opened.
|
||||||
char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWasUpdated, int shouldDebug);
|
char *updateJournal(char *path, char *journal, char *timeFormat, int *journalWasUpdated, int *returnNoteSelection, int shouldDebug);
|
||||||
#endif
|
#endif
|
||||||
@@ -1,160 +1,178 @@
|
|||||||
#include "ui.h"
|
#include "ui.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
void createNewVault(char *dirToVault, int bypass, char *bypassvalue, int shouldDebug) {
|
void createNewVault(char **directoriesArray, int directoryCount, char **vaultsArray, int vaultCount, int bypass, char *bypassvalue, int shouldDebug) {
|
||||||
int duplicateWarning = 0; // set to 1 later if the vault you tried to create already existed
|
int duplicateWarning = 0; // set to 1 later if the vault you tried to create already existed
|
||||||
int emptyWarning = 0; // set to 1 later if inpted empty name
|
int emptyWarning = 0; // set to 1 later if inpted empty name
|
||||||
input_screen:
|
input_screen:
|
||||||
char *vaultName = malloc(PATH_MAX);
|
// choose in which directory the vault will be created
|
||||||
if (!bypass) { // if won't bypass (if -v or --vault weren't set)
|
char *dirToVault =
|
||||||
echo();
|
ncursesSelect(directoriesArray, "Select a directory, in which the vault will remain (Use arrows or WASD, Enter to select or use 1-9 to quick select)", directoryCount, 0, "", "", " ", shouldDebug);
|
||||||
keypad(stdscr, FALSE);
|
char *vaultName = malloc(PATH_MAX);
|
||||||
// color code from https://stackoverflow.com/a/73396575
|
if (!bypass) { // if won't bypass (if -v or --vault weren't set)
|
||||||
start_color();
|
echo();
|
||||||
clear();
|
keypad(stdscr, FALSE);
|
||||||
use_default_colors();
|
// color code from https://stackoverflow.com/a/73396575
|
||||||
init_pair(1, COLOR_WHITE, -1); // -1 is blank
|
start_color();
|
||||||
init_pair(2, COLOR_RED, -1);
|
clear();
|
||||||
attron(COLOR_PAIR(1));
|
use_default_colors();
|
||||||
printw("Enter the name of the new vault: ");
|
init_pair(1, COLOR_WHITE, -1); // -1 is blank
|
||||||
mvprintw(3, 0, "Unsafe characters such as \\ and / will be replaced by _");
|
init_pair(2, COLOR_RED, -1);
|
||||||
attroff(COLOR_PAIR(1));
|
attron(COLOR_PAIR(1));
|
||||||
if (duplicateWarning) {
|
printw("Enter the name of the new vault: ");
|
||||||
attron(COLOR_PAIR(2));
|
mvprintw(3, 0, "Unsafe characters such as \\ and / will be replaced by _");
|
||||||
mvprintw(4, 0, "A vault with this name already exists. Please input another name");
|
attroff(COLOR_PAIR(1));
|
||||||
attroff(COLOR_PAIR(2));
|
if (duplicateWarning) {
|
||||||
|
attron(COLOR_PAIR(2));
|
||||||
|
mvprintw(4, 0, "A vault with this name already exists. Please input another name");
|
||||||
|
attroff(COLOR_PAIR(2));
|
||||||
|
}
|
||||||
|
if (emptyWarning) {
|
||||||
|
attron(COLOR_PAIR(2));
|
||||||
|
mvprintw(5, 0, "The vault's name must not be empty. Please input another name");
|
||||||
|
attroff(COLOR_PAIR(2));
|
||||||
|
}
|
||||||
|
move(1, 1); // replace cursor
|
||||||
|
wgetnstr(stdscr, vaultName, PATH_MAX - 1);
|
||||||
|
refresh();
|
||||||
|
endwin();
|
||||||
|
reset_shell_mode();
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stderr);
|
||||||
|
} else {
|
||||||
|
strncpy(vaultName, bypassvalue, PATH_MAX - 2); // -2 (and later -1) because indexing
|
||||||
|
vaultName[PATH_MAX - 1] = '\0';
|
||||||
}
|
}
|
||||||
if (emptyWarning) {
|
// check if no empty string
|
||||||
attron(COLOR_PAIR(2));
|
if (strcmp(vaultName, "") == 0) {
|
||||||
mvprintw(5, 0, "The vault's name must not be empty. Please input another name");
|
emptyWarning = 1;
|
||||||
attroff(COLOR_PAIR(2));
|
goto input_screen;
|
||||||
}
|
}
|
||||||
move(1, 1); // replace cursor
|
debug("Inputed vaultName=%s", vaultName);
|
||||||
wgetnstr(stdscr, vaultName, PATH_MAX-1);
|
sanitize(vaultName);
|
||||||
refresh();
|
debug("Sanitized vaultName=%s", vaultName);
|
||||||
endwin();
|
|
||||||
reset_shell_mode();
|
|
||||||
fflush(stdout);
|
|
||||||
fflush(stderr);
|
|
||||||
} else {
|
|
||||||
strncpy(vaultName, bypassvalue, PATH_MAX -2); // -2 (and later -1) because indexing
|
|
||||||
vaultName[PATH_MAX-1] = '\0';
|
|
||||||
}
|
|
||||||
// check if no empty string
|
|
||||||
if (strcmp(vaultName, "") == 0) {
|
|
||||||
emptyWarning = 1;
|
|
||||||
goto input_screen;
|
|
||||||
}
|
|
||||||
debug("Inputed vaultName=%s", vaultName);
|
|
||||||
sanitize(vaultName);
|
|
||||||
debug("Sanitized vaultName=%s", vaultName);
|
|
||||||
|
|
||||||
struct stat st = {0}; // https://stackoverflow.com/a/7430262
|
if (!isStringInArray(vaultName, (const char **)vaultsArray,
|
||||||
char vaultFullPath[PATH_MAX];
|
vaultCount)) { // if vault doesnt already exists. We avoid vaults with the
|
||||||
sprintf(vaultFullPath, "%s/%s/", dirToVault, vaultName);
|
// same name even if they are from different directories.
|
||||||
if (stat(vaultFullPath, &st) == -1) {
|
char vaultFullPath[PATH_MAX]; // recreating the full path
|
||||||
mkdir(vaultFullPath, 0744);
|
sprintf(vaultFullPath, "%s/%s/", dirToVault, vaultName);
|
||||||
} else {
|
|
||||||
// if stat(...) != -1 it means the vault already exist. We will go back to the input screen with a new message.
|
mkdir(vaultFullPath, 0744);
|
||||||
duplicateWarning = 1;
|
} else {
|
||||||
goto input_screen;
|
duplicateWarning = 1;
|
||||||
}
|
goto input_screen;
|
||||||
free(vaultName);
|
}
|
||||||
|
free(vaultName);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, char *bypassvalue, char *journalRegex, int shouldDebug) {
|
char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, char *bypassvalue, char *journalRegex, int shouldDebug) {
|
||||||
// input from user for the name
|
// input from user for the name
|
||||||
char *fileName = malloc(BUFFER_SIZE);
|
char *fileName = malloc(BUFFER_SIZE);
|
||||||
if (!bypass) { // if we don't bypass. (if -n or --note weren't set.)
|
if (!bypass) { // if we don't bypass. (if -n or --note weren't set.)
|
||||||
echo();
|
echo();
|
||||||
keypad(stdscr, FALSE);
|
keypad(stdscr, FALSE);
|
||||||
clear();
|
clear();
|
||||||
printw("Enter the name of the new note: ");
|
printw("Enter the name of the new note: ");
|
||||||
mvprintw(3, 0, "Unsafe characters such as \\, and / will be replaced by _");
|
mvprintw(3, 0, "Unsafe characters such as \\, and / will be replaced by _");
|
||||||
mvprintw(4, 0, "If the name matches with the regex for a journal (%s), it will create a journal instead of a note.", journalRegex);
|
mvprintw(4, 0,
|
||||||
move(1,1);
|
"If the name matches with the regex for a journal (%s), it will create a journal "
|
||||||
wgetnstr(stdscr, fileName, BUFFER_SIZE-4); //limits the buffer to prevent overflow (-4 to account indexing and from ".md" in case we need to add it later)
|
"instead of a note.",
|
||||||
refresh();
|
journalRegex);
|
||||||
endwin();
|
move(1, 1);
|
||||||
reset_shell_mode();
|
wgetnstr(stdscr, fileName,
|
||||||
fflush(stdout);
|
BUFFER_SIZE - 4); // limits the buffer to prevent overflow (-4 to account indexing
|
||||||
fflush(stderr);
|
// and from ".md" in case we need to add it later)
|
||||||
} else { // bypasses user input if we bypass is set to 1
|
refresh();
|
||||||
strncpy(fileName, bypassvalue, BUFFER_SIZE);
|
endwin();
|
||||||
fileName[BUFFER_SIZE-1] = '\0';
|
reset_shell_mode();
|
||||||
}
|
fflush(stdout);
|
||||||
error(strcmp(fileName, "") == 0, "user", "fileName is empty"); // replace this with a warning and add a warning if duplicate file and handle case where multiple warnings (if such case is possible)
|
fflush(stderr);
|
||||||
// check/sanitize the input
|
} else { // bypasses user input if we bypass is set to 1
|
||||||
debug("Inputed fileName=%s", fileName);
|
strncpy(fileName, bypassvalue, BUFFER_SIZE);
|
||||||
sanitize(fileName);
|
fileName[BUFFER_SIZE - 1] = '\0';
|
||||||
debug("Sanitized fileName=%s (We might append .md later", fileName);
|
|
||||||
// we trow an error if the file already exists (previous behaviour was overwriting the file)
|
|
||||||
char path[PATH_MAX];
|
|
||||||
snprintf(path, PATH_MAX, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
|
||||||
struct stat st;
|
|
||||||
error(stat(path, &st) == 0, "user", "%s already exists", fileName);
|
|
||||||
// if it matches with the journalRegex we treat it as a journal instead of a note
|
|
||||||
regex_t regex;
|
|
||||||
int regexReturn = regcomp(®ex, journalRegex, 0);
|
|
||||||
error(regexReturn, "program", "Regex compilation failed.");
|
|
||||||
regexReturn = regexec(®ex, fileName, 0, NULL, 0);
|
|
||||||
if (!regexReturn) {
|
|
||||||
debug("%s matches with %s treating it as a journal", fileName, journalRegex);
|
|
||||||
char **options = malloc(32); // the number of bytes is exactly what in the two strings
|
|
||||||
options[0] = "Divided journal";
|
|
||||||
options[1] = "Unified journal";
|
|
||||||
char *optionSelected = ncursesSelect(options, "Select which type of journal you want to create (Use arrows or WASD, Enter to select):", 2, 0, "", "", " ", shouldDebug);
|
|
||||||
debug("%s was selected to be a %s", fileName, optionSelected);
|
|
||||||
if (strcmp(optionSelected, options[0]) == 0) { // if it is a divided journal
|
|
||||||
char *fileFullPath = malloc(PATH_MAX);
|
|
||||||
snprintf(fileFullPath, PATH_MAX, "%s/%s/%s/", dirToVault, vaultFromDir, fileName);
|
|
||||||
struct stat st = {0};
|
|
||||||
if (stat(fileFullPath, &st) == -1) {
|
|
||||||
error(!mkdir(fileFullPath, 0744), "program", "mkdir failed");
|
|
||||||
} else {
|
|
||||||
error(1, "program", "%s could not be created", fileFullPath);
|
|
||||||
}
|
|
||||||
free(fileFullPath);
|
|
||||||
} else { // if it is a unified journal
|
|
||||||
char *fileFullPath = malloc(PATH_MAX);
|
|
||||||
snprintf(fileFullPath, PATH_MAX, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
|
||||||
int len = strlen(fileFullPath);
|
|
||||||
if (fileFullPath[len-3] != '.' || fileFullPath[len-2] != 'm' || fileFullPath[len-1] != 'd') { // there might be a cleaner way to do this
|
|
||||||
error(len > PATH_MAX - 3, "user", "%s is too big (greater than PATH_MAX-3) and we can't append .md", fileFullPath);
|
|
||||||
strncat(fileFullPath, ".md", PATH_MAX);
|
|
||||||
// we checked before if fileName didn't already exist.
|
|
||||||
// we must redo it as we add a .mode
|
|
||||||
struct stat st;
|
|
||||||
error(stat(fileFullPath, &st) == 0, "user", "%s already exists", fileFullPath);
|
|
||||||
}
|
|
||||||
FILE *filePointer;
|
|
||||||
filePointer = fopen(fileFullPath, "w"); // creates and opens the file
|
|
||||||
error(filePointer == NULL, "program", "The %s couldn't be created.", fileFullPath);
|
|
||||||
fprintf(filePointer, "### %s\n", fileName);
|
|
||||||
fclose(filePointer); // closes the file so that nvim could open it
|
|
||||||
free(fileFullPath);
|
|
||||||
}
|
}
|
||||||
free(options); // they are useless now
|
error(strcmp(fileName, "") == 0, "user",
|
||||||
} else { // normal process for a note
|
"fileName is empty"); // replace this with a warning and add a warning if duplicate file and
|
||||||
debug("%s does not match with %s treating it as a note", fileName, journalRegex);
|
// handle case where multiple warnings (if such case is possible)
|
||||||
// if there is no .md add an .md
|
// check/sanitize the input
|
||||||
int len = strlen(fileName);
|
debug("Inputed fileName=%s", fileName);
|
||||||
if (fileName[len-3] != '.' || fileName[len-2] != 'm' || fileName[len-1] != 'd') { // there might be a cleaner way to do this
|
sanitize(fileName);
|
||||||
strcat(fileName, ".md"); // this should not cause an overflow issue as we get at most 252 chars (+'.'+'m'+'d'+'\0' makes it to 256) with wgetnstr
|
debug("Sanitized fileName=%s (We might append .md later", fileName);
|
||||||
|
// we trow an error if the file already exists (previous behaviour was overwriting the file)
|
||||||
|
char path[PATH_MAX];
|
||||||
|
snprintf(path, PATH_MAX, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
||||||
|
struct stat st;
|
||||||
|
error(stat(path, &st) == 0, "user", "%s already exists", fileName);
|
||||||
|
// if it matches with the journalRegex we treat it as a journal instead of a note
|
||||||
|
regex_t regex;
|
||||||
|
int regexReturn = regcomp(®ex, journalRegex, 0);
|
||||||
|
error(regexReturn, "program", "Regex compilation failed.");
|
||||||
|
regexReturn = regexec(®ex, fileName, 0, NULL, 0);
|
||||||
|
if (!regexReturn) {
|
||||||
|
debug("%s matches with %s treating it as a journal", fileName, journalRegex);
|
||||||
|
char **options = malloc(32); // the number of bytes is exactly what in the two strings
|
||||||
|
options[0] = "Divided journal";
|
||||||
|
options[1] = "Unified journal";
|
||||||
|
char *optionSelected = ncursesSelect(options,
|
||||||
|
"Select which type of journal you want to create "
|
||||||
|
"(Use arrows or WASD, Enter to select):",
|
||||||
|
2, 0, "", "", " ", shouldDebug);
|
||||||
|
debug("%s was selected to be a %s", fileName, optionSelected);
|
||||||
|
if (strcmp(optionSelected, options[0]) == 0) { // if it is a divided journal
|
||||||
|
char *fileFullPath = malloc(PATH_MAX);
|
||||||
|
snprintf(fileFullPath, PATH_MAX, "%s/%s/%s/", dirToVault, vaultFromDir, fileName);
|
||||||
|
struct stat st = {0};
|
||||||
|
if (stat(fileFullPath, &st) == -1) {
|
||||||
|
error(mkdir(fileFullPath, 0744), "program", "mkdir failed");
|
||||||
|
} else {
|
||||||
|
error(1, "program", "%s could not be created", fileFullPath);
|
||||||
|
}
|
||||||
|
free(fileFullPath);
|
||||||
|
} else { // if it is a unified journal
|
||||||
|
char *fileFullPath = malloc(PATH_MAX);
|
||||||
|
// we append .md directly to fileName and not only to fileFullPath to avoid passing the wrong fileName later.
|
||||||
|
int lenName = strlen(fileName);
|
||||||
|
if (fileName[lenName - 3] != '.' && fileName[lenName - 2] != 'm' && fileName[lenName - 2] != '1') {
|
||||||
|
strcat(fileName, ".md");
|
||||||
|
debug("We just appended .md to %s", fileName);
|
||||||
|
}
|
||||||
|
snprintf(fileFullPath, PATH_MAX, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
||||||
|
// we verify if the file doesn't already exist as we might just have changed the fileName and (by extension) the file path.
|
||||||
|
struct stat metadata;
|
||||||
|
error(stat(fileFullPath, &metadata) == 0, "user", "%s already exists", fileFullPath);
|
||||||
|
FILE *filePointer;
|
||||||
|
filePointer = fopen(fileFullPath, "w"); // creates and opens the file
|
||||||
|
error(filePointer == NULL, "program", "The %s couldn't be created.", fileFullPath);
|
||||||
|
fprintf(filePointer, "### %s\n", fileName);
|
||||||
|
fclose(filePointer); // closes the file so that nvim could open it
|
||||||
|
free(fileFullPath);
|
||||||
|
}
|
||||||
|
free(options); // they are useless now
|
||||||
|
} else { // normal process for a note
|
||||||
|
debug("%s does not match with %s treating it as a note", fileName, journalRegex);
|
||||||
|
// if there is no .md add an .md
|
||||||
|
int len = strlen(fileName);
|
||||||
|
if (fileName[len - 3] != '.' || fileName[len - 2] != 'm' || fileName[len - 1] != 'd') { // there might be a cleaner way to do this
|
||||||
|
strcat(fileName, ".md"); // this should not cause an overflow issue as we get at most
|
||||||
|
// 252 chars (+'.'+'m'+'d'+'\0' makes it to 256) with wgetnstr
|
||||||
|
}
|
||||||
|
char *fileFullPath = malloc(PATH_MAX); // this dinamically allocated because we use it in
|
||||||
|
// the main function to call openEditor
|
||||||
|
sprintf(fileFullPath, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
||||||
|
FILE *filePointer;
|
||||||
|
filePointer = fopen(fileFullPath, "w"); // creates and opens the file
|
||||||
|
error(filePointer == NULL, "program", "The %s couldn't be created.", fileFullPath);
|
||||||
|
fprintf(filePointer, "### %s\n", fileName);
|
||||||
|
fclose(filePointer); // closes the file so that nvim could open it
|
||||||
|
free(fileFullPath);
|
||||||
}
|
}
|
||||||
char *fileFullPath = malloc(PATH_MAX); // this dinamically allocated because we use it in the main function to call openEditor
|
return fileName;
|
||||||
sprintf(fileFullPath, "%s/%s/%s", dirToVault, vaultFromDir, fileName);
|
|
||||||
FILE *filePointer;
|
|
||||||
filePointer = fopen(fileFullPath, "w"); // creates and opens the file
|
|
||||||
error(filePointer == NULL, "program", "The %s couldn't be created.", fileFullPath);
|
|
||||||
fprintf(filePointer, "### %s\n", fileName);
|
|
||||||
fclose(filePointer); // closes the file so that nvim could open it
|
|
||||||
free(fileFullPath);
|
|
||||||
}
|
|
||||||
return fileName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
char *fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
||||||
// we are gonna write each line of each files (with the filename and the line number to an index)
|
// we are gonna write each line of each files (with the filename and the line number to an
|
||||||
|
// index)
|
||||||
char command[CMD_BUFFER];
|
char command[CMD_BUFFER];
|
||||||
char indexFile[] = "/tmp/notewrapper_index_XXXXXX";
|
char indexFile[] = "/tmp/notewrapper_index_XXXXXX";
|
||||||
|
|
||||||
@@ -162,17 +180,7 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
|||||||
error(fd == -1, "program", "mkstemp failed");
|
error(fd == -1, "program", "mkstemp failed");
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
/*
|
snprintf(command, sizeof(command), "rg --line-number --no-heading --color=never . \"%s\" > %s", pathToFiles, indexFile);
|
||||||
* STEP 1:
|
|
||||||
* Build index once into temp file
|
|
||||||
* format: file:line:content
|
|
||||||
*/
|
|
||||||
snprintf(command, sizeof(command),
|
|
||||||
"rg --line-number --no-heading --color=never . \"%s\" > %s",
|
|
||||||
pathToFiles,
|
|
||||||
indexFile
|
|
||||||
);
|
|
||||||
|
|
||||||
debug("INDEX CMD: %s", command);
|
debug("INDEX CMD: %s", command);
|
||||||
|
|
||||||
if (system(command) != 0) {
|
if (system(command) != 0) {
|
||||||
@@ -180,53 +188,13 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
|||||||
error(1, "program", "rg indexing failed");
|
error(1, "program", "rg indexing failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* STEP 2:
|
|
||||||
* Use fzf on the index file (NO rg anymore)
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
EXPLANATION:
|
|
||||||
|
|
||||||
cat %s
|
|
||||||
→ feeds prebuilt index file (format: file:line:content) into fzf
|
|
||||||
|
|
||||||
fzf --delimiter ':'
|
|
||||||
→ splits each line into fields:
|
|
||||||
{1} = file path
|
|
||||||
{2} = line number
|
|
||||||
{3} = content
|
|
||||||
|
|
||||||
--prompt
|
|
||||||
→ UI prompt text shown in fzf
|
|
||||||
|
|
||||||
--preview
|
|
||||||
→ runs a shell command for selected item:
|
|
||||||
file={1} → current file
|
|
||||||
line={2} → line number of match
|
|
||||||
|
|
||||||
nl -ba "$file"
|
|
||||||
→ prints file with line numbers
|
|
||||||
|
|
||||||
sed -n "$((line-5)),$((line+5))p"
|
|
||||||
→ extracts a window of 5 lines above and below match
|
|
||||||
|
|
||||||
sed "... -> ..."
|
|
||||||
→ highlights the matched line (middle of window) with red arrow
|
|
||||||
|
|
||||||
--preview-window=right:60%:hidden
|
|
||||||
→ preview appears on right, 60% width, initially hidden
|
|
||||||
|
|
||||||
--bind 'change:show-preview'
|
|
||||||
→ preview only appears after first selection change (not at startup)
|
|
||||||
*/
|
|
||||||
snprintf(command, sizeof(command),
|
snprintf(command, sizeof(command),
|
||||||
"cat %s | fzf --delimiter ':' --prompt='%s' "
|
"cat %s | fzf --delimiter ':' --prompt='%s' "
|
||||||
"--preview 'file={1}; line={2}; nl -ba \"$file\" | sed -n \"$((line-5)),$((line+5))p\" | sed \"$((line-$(($((line-5))))+1))s/^/\\x1b[31m-> \\x1b[0m/\"' "
|
"--preview 'file={1}; line={2}; nl -ba \"$file\" | sed -n \"$((line-5)),$((line+5))p\" | "
|
||||||
|
"sed \"$((line-$(($((line-5))))+1))s/^/\\x1b[31m-> \\x1b[0m/\"' "
|
||||||
"--preview-window=right:60%%:hidden "
|
"--preview-window=right:60%%:hidden "
|
||||||
"--bind 'change:show-preview'",
|
"--bind 'change:show-preview'",
|
||||||
indexFile,
|
indexFile, selectText ? selectText : "> ");
|
||||||
selectText ? selectText : "> "
|
|
||||||
);
|
|
||||||
debug("FZF CMD: %s", command);
|
debug("FZF CMD: %s", command);
|
||||||
|
|
||||||
FILE *fzfPipe = popen(command, "r");
|
FILE *fzfPipe = popen(command, "r");
|
||||||
@@ -242,16 +210,25 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
|||||||
buffer[strcspn(buffer, "\n")] = '\0';
|
buffer[strcspn(buffer, "\n")] = '\0';
|
||||||
result = strdup(buffer);
|
result = strdup(buffer);
|
||||||
}
|
}
|
||||||
|
debug("Buffer: %s", buffer);
|
||||||
|
debug("result: %s", result);
|
||||||
|
|
||||||
// cut at first ':'
|
// cut at first ':'
|
||||||
result = strtok(result, ":");
|
result = strtok(result, ":");
|
||||||
// cut at / (because we need the full paths before and we only need to return the journal entry)
|
// we need to cut out the whole pathToFiles
|
||||||
char *token = strtok(result, "/");
|
int pathToFilesLen = strlen(pathToFiles);
|
||||||
result = NULL;
|
int cutOut = 0;
|
||||||
while (token != NULL) {
|
for (int i = 0; i < pathToFilesLen; i++) {
|
||||||
result = token;
|
if (pathToFiles[i] == result[i]) {
|
||||||
token = strtok(NULL, "/"); //Passing NULL means “don’t start a new string, resume
|
cutOut = i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
result += cutOut + 1; // the + 1 cuts the first /
|
||||||
|
|
||||||
|
// in case we select a journal, we won't open the entry just yet. We only need to return the journal name (everything before [now the first] /
|
||||||
|
result = strtok(result, "/");
|
||||||
|
|
||||||
|
debug("Cleaned note: %s", result);
|
||||||
|
|
||||||
// clean up
|
// clean up
|
||||||
pclose(fzfPipe);
|
pclose(fzfPipe);
|
||||||
@@ -260,81 +237,147 @@ char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* ncursesSelect(char **options, char *optionsText, int optionsNumber, int extraOptionsNumber, char *bottomText, char *middleText, char *topText, int shouldDebug) {
|
char *ncursesSelect(char **options, char *optionsText, int optionsNumber, int extraOptionsNumber, char *bottomText, char *middleText, char *topText,
|
||||||
int highlight = 0; //curently highlighted option
|
int shouldDebug) { // TODO we should see how it handles larges strings (like large directories)
|
||||||
|
int highlight = 0; // curently highlighted option
|
||||||
int key;
|
int key;
|
||||||
|
|
||||||
cbreak(); // disable line buffering
|
cbreak(); // disable line buffering
|
||||||
noecho(); // don't echo key presses
|
noecho(); // don't echo key presses
|
||||||
keypad(stdscr, TRUE); // enable arrow keys
|
keypad(stdscr, TRUE); // enable arrow keys
|
||||||
start_color();
|
start_color();
|
||||||
curs_set(0); // sets the cursor to invisible. (We will emulate the cursor with a hightlight that go up and down).
|
curs_set(0); // sets the cursor to invisible. (We will emulate the cursor with a hightlight that
|
||||||
|
// go up and down).
|
||||||
use_default_colors();
|
use_default_colors();
|
||||||
init_pair(1, COLOR_WHITE, -1); // color for optionsNumber (so notes, vaults, etc.)
|
init_pair(1, COLOR_WHITE, -1); // color for optionsNumber (so notes, vaults, etc.)
|
||||||
init_pair(2, COLOR_BLUE, -1); // color for extraOptionsNumber (so settings, delete notes, create vault, etc.)
|
init_pair(2, COLOR_BLUE,
|
||||||
|
-1); // color for extraOptionsNumber (so settings, delete notes, create vault, etc.)
|
||||||
while (1) {
|
while (1) {
|
||||||
clear();
|
clear();
|
||||||
attron(COLOR_PAIR(1));
|
attron(COLOR_PAIR(1));
|
||||||
mvprintw(0,0, "%s", optionsText);
|
mvprintw(0, 0, "%s", optionsText);
|
||||||
int offset = 1;
|
int offset = 1;
|
||||||
if (strcmp(bottomText, "") != 0) {
|
if (strcmp(bottomText, "") != 0) {
|
||||||
mvprintw(offset, 1, "%s", bottomText);
|
mvprintw(offset, 1, "%s", bottomText);
|
||||||
offset++;
|
offset++;
|
||||||
}
|
|
||||||
// Print options with highlighting
|
|
||||||
for (int i = 0; i < optionsNumber; i++) {
|
|
||||||
if (i == highlight) {
|
|
||||||
attron(A_REVERSE);
|
|
||||||
} // highlight selected
|
|
||||||
mvprintw(i+offset, 2, "%s", options[i]);
|
|
||||||
if (i == highlight) {
|
|
||||||
attroff(A_REVERSE);
|
|
||||||
}
|
}
|
||||||
}
|
// Print options with highlighting
|
||||||
attroff(COLOR_PAIR(1));
|
for (int i = 0; i < optionsNumber; i++) {
|
||||||
if (strcmp(middleText, "") != 0) {
|
if (i == highlight) {
|
||||||
mvprintw(offset+optionsNumber, 1, "%s", middleText);
|
attron(A_REVERSE);
|
||||||
offset++;
|
} // highlight selected
|
||||||
}
|
mvprintw(i + offset, 2, "%s", options[i]);
|
||||||
// Printf extraOptions with highlighting
|
if (i == highlight) {
|
||||||
attron(COLOR_PAIR(2));
|
attroff(A_REVERSE);
|
||||||
for (int k = optionsNumber; k < optionsNumber + extraOptionsNumber; k++) {
|
}
|
||||||
if (k == highlight) {
|
|
||||||
attron(A_REVERSE);
|
|
||||||
}
|
}
|
||||||
mvprintw(k+offset, 2, "%s", options[k]);
|
attroff(COLOR_PAIR(1));
|
||||||
if (k == highlight) {
|
if (strcmp(middleText, "") != 0) {
|
||||||
attroff(A_REVERSE);
|
mvprintw(offset + optionsNumber, 1, "%s", middleText);
|
||||||
|
offset++;
|
||||||
}
|
}
|
||||||
}
|
// Printf extraOptions with highlighting
|
||||||
attroff(COLOR_PAIR(2));
|
attron(COLOR_PAIR(2));
|
||||||
attron(COLOR_PAIR(1));
|
for (int k = optionsNumber; k < optionsNumber + extraOptionsNumber; k++) {
|
||||||
mvprintw(offset+optionsNumber+extraOptionsNumber, 1, "%s", topText);
|
if (k == highlight) {
|
||||||
attroff(COLOR_PAIR(1));
|
attron(A_REVERSE);
|
||||||
key = getch(); //get some int which value correspond to some key being pressed
|
}
|
||||||
|
mvprintw(k + offset, 2, "%s", options[k]);
|
||||||
|
if (k == highlight) {
|
||||||
|
attroff(A_REVERSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
attroff(COLOR_PAIR(2));
|
||||||
|
attron(COLOR_PAIR(1));
|
||||||
|
mvprintw(offset + optionsNumber + extraOptionsNumber, 1, "%s", topText);
|
||||||
|
attroff(COLOR_PAIR(1));
|
||||||
|
key = getch(); // get some int which value correspond to some key being pressed
|
||||||
|
|
||||||
switch(key) {
|
switch (key) {
|
||||||
case KEY_UP:
|
case KEY_UP:
|
||||||
case 'w':
|
case 'w':
|
||||||
case 'W':
|
case 'W':
|
||||||
highlight--;
|
highlight--;
|
||||||
if (highlight < 0) {
|
if (highlight < 0) {
|
||||||
highlight = optionsNumber + extraOptionsNumber - 1;// can't select the -1nth option
|
highlight = optionsNumber + extraOptionsNumber - 1; // can't select the -1nth option
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
case 'S':
|
case 'S':
|
||||||
case KEY_DOWN:
|
case KEY_DOWN:
|
||||||
highlight++;
|
highlight++;
|
||||||
if (highlight >= optionsNumber + extraOptionsNumber) {
|
if (highlight >= optionsNumber + extraOptionsNumber) {
|
||||||
highlight = 0;
|
highlight = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case '1':
|
||||||
|
if (optionsNumber + extraOptionsNumber >= 1) {
|
||||||
|
debug("Quickselected option 1.");
|
||||||
|
highlight = 0;
|
||||||
|
goto end_loop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
if (optionsNumber + extraOptionsNumber >= 1) {
|
||||||
|
debug("Quickselected option 2.");
|
||||||
|
highlight = 1;
|
||||||
|
goto end_loop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
if (optionsNumber + extraOptionsNumber >= 1) {
|
||||||
|
debug("Quickselected option 3.");
|
||||||
|
highlight = 2;
|
||||||
|
goto end_loop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
if (optionsNumber + extraOptionsNumber >= 1) {
|
||||||
|
debug("Quickselected option 4.");
|
||||||
|
highlight = 3;
|
||||||
|
goto end_loop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '5':
|
||||||
|
if (optionsNumber + extraOptionsNumber >= 1) {
|
||||||
|
debug("Quickselected option 5.");
|
||||||
|
highlight = 4;
|
||||||
|
goto end_loop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
if (optionsNumber + extraOptionsNumber >= 1) {
|
||||||
|
debug("Quickselected option 6.");
|
||||||
|
highlight = 5;
|
||||||
|
goto end_loop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '7':
|
||||||
|
if (optionsNumber + extraOptionsNumber >= 1) {
|
||||||
|
debug("Quickselected option 7.");
|
||||||
|
highlight = 6;
|
||||||
|
goto end_loop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '8':
|
||||||
|
if (optionsNumber + extraOptionsNumber >= 1) {
|
||||||
|
debug("Quickselected option 8.");
|
||||||
|
highlight = 7;
|
||||||
|
goto end_loop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '9':
|
||||||
|
if (optionsNumber + extraOptionsNumber >= 1) {
|
||||||
|
debug("Quickselected option 9.");
|
||||||
|
highlight = 8;
|
||||||
|
goto end_loop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 10: //\n
|
case 10: //\n
|
||||||
goto end_loop;
|
goto end_loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end_loop:
|
end_loop:
|
||||||
endwin(); // end ncurses mode
|
endwin(); // end ncurses mode
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
debug("Selected option: %s", options[highlight]);
|
debug("Selected option: %s", options[highlight]);
|
||||||
|
|||||||
@@ -1,45 +1,44 @@
|
|||||||
#ifndef UI_H
|
#ifndef UI_H
|
||||||
#define UI_H
|
#define UI_H
|
||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <ncurses.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <regex.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ncurses.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <pwd.h>
|
#include <unistd.h>
|
||||||
#include <ctype.h>
|
#define CMD_BUFFER 4096 // for fzfSelect
|
||||||
#include <limits.h>
|
|
||||||
#include <regex.h>
|
|
||||||
#include "utils.h"
|
|
||||||
#define CMD_BUFFER 4096 // for fzfSelect
|
|
||||||
#define RESULT_BUFFER 1024 // for fzfSelect
|
#define RESULT_BUFFER 1024 // for fzfSelect
|
||||||
|
|
||||||
/*Uses ncurses to get an input from the user.
|
/*Uses ncurses to get an input from the user.
|
||||||
Creates a new note with this input.
|
Creates a new note with this input.
|
||||||
also can create a journal if the name matches with journalRegex.
|
also can create a journal if the name matches with journalRegex.
|
||||||
returns the path to the note.
|
returns the path to the note.
|
||||||
If you want to bypass the input TUI set bypass to 1 and bypassvalue to the note name
|
If you want to bypass the input TUI set bypass to 1 and bypassvalue to the note name
|
||||||
*/
|
*/
|
||||||
char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, char *bypassvalue, char *journalRegex, int debug);
|
char *createNewNote(char dirToVault[PATH_MAX], char *vaultFromDir, int bypass, char *bypassvalue, char *journalRegex, int debug);
|
||||||
/*Uses ncurses to get an input from the user.
|
/*
|
||||||
Creates a new vault with this input.
|
Chooses in which directory it want to create the vault.
|
||||||
If vault already exists, prints a warning.
|
Uses ncurses to get an input from the user.
|
||||||
|
Creates a new vault with this input.
|
||||||
|
If vault already exists, prints a warning.
|
||||||
returns nothing. */
|
returns nothing. */
|
||||||
void createNewVault(char *dirToVault, int bypass, char *bypassvalue, int debug);
|
void createNewVault(char **directoriesArray, int directoryCount, char **vaultsArray, int vaultCount, int bypass, char *bypassvalue, int debug);
|
||||||
// uses fzf and ripgrep to search inside the files to choose between all the options.
|
// uses fzf and ripgrep to search inside the files to choose between all the options.
|
||||||
char* fzfSelect(char *pathToFiles, char *selectText, int shouldDebug);
|
char *fzfSelect(char *pathToFiles, char *selectText, int shouldDebug);
|
||||||
/*this function is used multiple times let the user select one options from many with ncurses in a TUI.
|
/*this function is used multiple times let the user select one options from many with ncurses in a
|
||||||
options is the array of strings with all the options.
|
TUI. options is the array of strings with all the options. optionsText is the text that will be
|
||||||
optionsText is the text that will be printed at the top (For example: "Please select ...").
|
printed at the top (For example: "Please select ..."). we distinguish options from extraOptions.
|
||||||
we distinguish options from extraOptions.
|
|
||||||
options could be the list of all notes or all vaults.
|
options could be the list of all notes or all vaults.
|
||||||
extraOptions are options that are special and have a special color (for ex: "Delete vault", "Settings", etc.).
|
extraOptions are options that are special and have a special color (for ex: "Delete vault",
|
||||||
note: extraOptions should be at the end of options.
|
"Settings", etc.). note: extraOptions should be at the end of options. topText is printed between
|
||||||
topText is printed between options text and the start of options.
|
options text and the start of options. middleText (usally \n) is printed between options and
|
||||||
middleText (usally \n) is printed between options and extraOptions.
|
extraOptions. bottomText is printed bellow. topText and middle must be exactly one line. If you want
|
||||||
bottomText is printed bellow.
|
empty lines use " " and not "". returns the selected option. */
|
||||||
topText and middle must be exactly one line. If you want empty lines use " " and not "".
|
char *ncursesSelect(char **options, char *optionsText, int optionsNumber, int extraOptionsNumber, char *bottomText, char *middleText, char *topText, int debug);
|
||||||
returns the selected option. */
|
|
||||||
char* ncursesSelect(char **options, char *optionsText, int optionsNumber, int extraOptionsNumber, char *bottomText, char *middleText, char *topText, int debug);
|
|
||||||
#endif
|
#endif
|
||||||
+582
-347
File diff suppressed because it is too large.
Load diff
+47
-31
@@ -1,52 +1,55 @@
|
|||||||
#ifndef UTILS_H
|
#ifndef UTILS_H
|
||||||
#define UTILS_H
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include <cjson/cJSON.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ftw.h>
|
||||||
|
#include <git2.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <ncurses.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <regex.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <dirent.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <ftw.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include <ncurses.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <limits.h>
|
|
||||||
#include <cjson/cJSON.h>
|
|
||||||
#include <regex.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <ftw.h>
|
#include <unistd.h>
|
||||||
#define BUFFER_SIZE 256 //standard buffer size.
|
|
||||||
// the three supported values are "daily" "weekly" and "monthly" (for months we use the avarage lenght of a month in a non leap year). All values were calculated on my loyal TI-30 ECO RS
|
#ifndef VERSION
|
||||||
|
#define VERSION "dev"
|
||||||
|
#endif
|
||||||
|
#define BUFFER_SIZE 256 // standard buffer size.
|
||||||
|
// the three supported values are "daily" "weekly" and "monthly" (for months we use the avarage
|
||||||
|
// lenght of a month in a non leap year). All values were calculated on my loyal TI-30 ECO RS
|
||||||
#define DAILY 86400
|
#define DAILY 86400
|
||||||
#define WEEKLY 604800
|
#define WEEKLY 604800
|
||||||
#define MONTHLY 2628000 // calculated from the average lenght of a non leap year
|
#define MONTHLY 2628000 // calculated from the average lenght of a non leap year
|
||||||
#define debug(message, ...) \
|
#define debug(message, ...) _debug(shouldDebug, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
||||||
_debug(shouldDebug, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
#define altDebug(message, ...) _altDebug(shouldDebug, message, ##__VA_ARGS__)
|
||||||
#define altDebug(message, ...) \
|
#define error(condition, type, message, ...) _error(shouldDebug, condition, type, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
||||||
_altDebug(shouldDebug, message, ##__VA_ARGS__)
|
|
||||||
#define error(condition, type, message, ...) \
|
|
||||||
_error(shouldDebug, condition, type, __FILE__, __LINE__, __func__, message, ##__VA_ARGS__)
|
|
||||||
// you must edit this two values if you want to add suport for an editor
|
// you must edit this two values if you want to add suport for an editor
|
||||||
extern const char *supportedEditor[]; // array of supported editors
|
extern const char *supportedEditor[]; // array of supported editors
|
||||||
extern const int numEditors; // number of supported editors
|
extern const int numEditors; // number of supported editors
|
||||||
|
|
||||||
|
// compares two strings alphabetically.
|
||||||
//compares two strings alphabetically.
|
// this function is used for qsort
|
||||||
//this function is used for qsort
|
|
||||||
int compareString(const void *a, const void *b);
|
int compareString(const void *a, const void *b);
|
||||||
// compares two strings in reversed alphabetical order
|
// compares two strings in reversed alphabetical order
|
||||||
// this function is used for qsort
|
// this function is used for qsort
|
||||||
int reverseCompareString(const void *a, const void *b);
|
int reverseCompareString(const void *a, const void *b);
|
||||||
|
// checks if editor is supported and if it installed.
|
||||||
// this basically checks all the dirs from your path for the editor. This is a safety check.
|
// this basically checks all the dirs from your path for the editor. This is a safety check.
|
||||||
// If the executable from an editor is not the editor name (for example neovim and nvim), you must handle at the start of the function.
|
// If the executable from an editor is not the editor name (for example neovim and nvim), you must
|
||||||
int doesEditorExist(char *editorToCheck, int debug);
|
// handle at the start of the function. This can return an error and stop the program.
|
||||||
|
int isEditorValid(char *editorToCheck, int useDefaultEditor, int debug);
|
||||||
// please use the macro debug instead of _debug.
|
// please use the macro debug instead of _debug.
|
||||||
//formated debugging.
|
// formated debugging.
|
||||||
void _debug(const int d, const char *file, const int line, const char *function, const char *message, ...);
|
void _debug(const int d, const char *file, const int line, const char *function, const char *message, ...);
|
||||||
//use for less formal debuggin. (usefull if enumerating or making a list).
|
// use for less formal debuggin. (usefull if enumerating or making a list).
|
||||||
void _altDebug(const int d, const char *message, ...);
|
void _altDebug(const int d, const char *message, ...);
|
||||||
// formated error.
|
// formated error.
|
||||||
void _error(const int shouldDebug, const int condition, const char *type, const char *file, const int line, const char *function, const char *message, ...);
|
void _error(const int shouldDebug, const int condition, const char *type, const char *file, const int line, const char *function, const char *message, ...);
|
||||||
@@ -63,8 +66,8 @@ void appendToFile(const char *path, const char *string, const int shouldDebug);
|
|||||||
// replace unwanted chars by '_'.
|
// replace unwanted chars by '_'.
|
||||||
// '.' is replaced if it is only the first two chars
|
// '.' is replaced if it is only the first two chars
|
||||||
void sanitize(char *string);
|
void sanitize(char *string);
|
||||||
//from https://stackoverflow.com/a/5467788.
|
// from https://stackoverflow.com/a/5467788.
|
||||||
//deletes an entire directory. Use with parsimony and carefullness.
|
// deletes an entire directory. Use with parsimony and carefullness.
|
||||||
int rmrf(char *path, int shouldDebug);
|
int rmrf(char *path, int shouldDebug);
|
||||||
// Inputs are the path to the file, the editor to open and some rendering option.
|
// Inputs are the path to the file, the editor to open and some rendering option.
|
||||||
// render: if we render the .md file with Vivify.
|
// render: if we render the .md file with Vivify.
|
||||||
@@ -77,6 +80,19 @@ int openEditor(char *path, char *editor, int render, int shouldJumpToEndOfFile,
|
|||||||
char *getFormatedTime(char *format, int shouldDebug);
|
char *getFormatedTime(char *format, int shouldDebug);
|
||||||
/* Caclulates if we need to do another backup (by reading ~/.cache/NoteWrapper/backupTime.txt.
|
/* Caclulates if we need to do another backup (by reading ~/.cache/NoteWrapper/backupTime.txt.
|
||||||
If need launches in the background rsync to do the backuping.
|
If need launches in the background rsync to do the backuping.
|
||||||
rsyncArgs is the array of arguments to be passed to rsync. Do not inclue destination or source. It will be added in the function.*/
|
Each pair of source/destination will launch one rsync process.
|
||||||
void handleBackups(const char *pathOfVaults, const char *pathOfBackup, const char *homeDir, const int interval, const char **rsyncArgs, const int rsyncArgsNumber, const int shouldDebug);
|
If a pair don't need to be backed up, the destination should be set to NULL.
|
||||||
|
rsyncArgs is the array of arguments to be passed to rsync. Do not inclue destination or source. It
|
||||||
|
will be added in the function.
|
||||||
|
backupMessage will be set to 1 if a backup was launched.*/
|
||||||
|
void handleBackups(char **sourceDirectoryArray, const int sourceDirectoryNumber, char **destinationDirectoryArray, const char *homeDir, const int interval, const char **rsyncArgs, const int rsyncArgsNumber,
|
||||||
|
const int forceBackup, int *backupMessage, const int shouldDebug);
|
||||||
|
/*Equivalent of git init.
|
||||||
|
If the directory wasn't git initialized. We do it.*/
|
||||||
|
void ensureGitDirectory(const char *path, const char *vault, const char *signatureName, const char *signatureEmail, const int shouldDebug);
|
||||||
|
/*Configures git, stages everything, commits everything*/
|
||||||
|
void gitBackupUpdate(const char *path, const char *vault, const char *signatureName, const char *signatureEmail, const char *commitMsg, const int shouldDebug);
|
||||||
|
// yyyy-mm-dd hh:mm:ss
|
||||||
|
// Don't forget to free the pointer.
|
||||||
|
char *getDateAndTime();
|
||||||
#endif
|
#endif
|
||||||
Reference in new issue
Block a user