updating all of my config, specifically doing this to fix the nvim configurations
This commit is contained in:
287
nvim/scripts/new.sh
Executable file
287
nvim/scripts/new.sh
Executable file
@@ -0,0 +1,287 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Exit immediately if a command exits with a non-zero status,
|
||||
# Treat unset variables as an error,
|
||||
# Prevent errors in a pipeline from being masked
|
||||
set -euo pipefail
|
||||
|
||||
# ============================
|
||||
# Configuration Constants
|
||||
# ============================
|
||||
|
||||
SOURCE_DIR="$HOME/vimwiki" # Source Vimwiki directory
|
||||
TEMP_DIR="/tmp/vimwikihtml" # Temporary HTML output directory
|
||||
CSS_FILE="$HOME/.local/share/nvim/style.css" # Path to the CSS file for styling
|
||||
CONCURRENT_JOBS=4 # Number of concurrent pandoc processes
|
||||
LOG_FILE="$TEMP_DIR/conversion.log" # Log file to track conversions
|
||||
ERROR_LOG_FILE="$TEMP_DIR/error.log" # Log file to track errors
|
||||
|
||||
# ============================
|
||||
# Dependencies
|
||||
# ============================
|
||||
|
||||
DEPENDENCIES=("pandoc" "sed" "qutebrowser" "find" "rsync" "grep" "mkdir" "basename" "diff")
|
||||
|
||||
# ============================
|
||||
# Function Definitions
|
||||
# ============================
|
||||
|
||||
# Function to check if all dependencies are installed
|
||||
check_dependencies() {
|
||||
echo "Checking for required dependencies..."
|
||||
for cmd in "${DEPENDENCIES[@]}"; do
|
||||
if ! command -v "$cmd" &>/dev/null; then
|
||||
echo "Error: '$cmd' is not installed. Please install it and retry."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "All dependencies are satisfied."
|
||||
}
|
||||
|
||||
# Function to extract the title from Markdown using YAML frontmatter or fallback to filename
|
||||
extract_title() {
|
||||
local md_file="$1"
|
||||
# Attempt to extract title from YAML frontmatter
|
||||
local title
|
||||
title=$(grep -m1 '^title:' "$md_file" | sed 's/title: //') || true
|
||||
if [[ -z "$title" ]]; then
|
||||
# If no title found, use the filename without extension
|
||||
title=$(basename "$md_file" .md.old)
|
||||
fi
|
||||
echo "$title"
|
||||
}
|
||||
|
||||
# Function to convert a single Markdown file to HTML
|
||||
convert_md_to_html() {
|
||||
local md_old_file="$1" # Path to the .md.old file
|
||||
# Determine the relative path from TEMP_DIR
|
||||
local relative_path="${md_old_file#$TEMP_DIR/}"
|
||||
# Determine the output HTML file path
|
||||
local html_file="$TEMP_DIR/${relative_path%.md.old}.html"
|
||||
# Create the necessary directories for the HTML file
|
||||
mkdir -p "$(dirname "$html_file")"
|
||||
|
||||
# Extract the title for the HTML document
|
||||
local title
|
||||
title=$(extract_title "$md_old_file")
|
||||
|
||||
echo "Converting '$md_old_file' to '$html_file'..."
|
||||
|
||||
# Use pandoc to convert Markdown to HTML with CSS and metadata
|
||||
if [[ -f "$CSS_FILE" ]]; then
|
||||
pandoc -f markdown -s --css="$CSS_FILE" --metadata title="$title" "$md_old_file" -o "$html_file" || {
|
||||
echo "Error: Failed to convert '$md_old_file' to HTML." | tee -a "$ERROR_LOG_FILE"
|
||||
return 1
|
||||
}
|
||||
else
|
||||
echo "Warning: CSS file '$CSS_FILE' not found. Skipping CSS for '$md_old_file'."
|
||||
pandoc -f markdown -s --metadata title="$title" "$md_old_file" -o "$html_file" || {
|
||||
echo "Error: Failed to convert '$md_old_file' to HTML." | tee -a "$ERROR_LOG_FILE"
|
||||
return 1
|
||||
}
|
||||
fi
|
||||
|
||||
# Debug: Print a snippet of the HTML file before running sed
|
||||
echo "Snippet before sed in '$html_file':"
|
||||
head -n 5 "$html_file"
|
||||
echo "..."
|
||||
|
||||
# Adjust internal href links:
|
||||
# 1. Replace href="path/to/file.md.old" with href="path/to/file.html"
|
||||
# 2. Replace href="path/to/file" with href="path/to/file.html" only if 'file' has no extension
|
||||
echo "Adjusting links in '$html_file'..."
|
||||
|
||||
# First, replace links ending with .md.old
|
||||
if ! sed -i -E 's|(href=")([^"#:/]+(/[^"#:/]+)*)\.md\.old(")|\1\2.html\4|g' "$html_file"; then
|
||||
echo "Error: Failed to adjust '.md.old' links in '$html_file'." | tee -a "$ERROR_LOG_FILE"
|
||||
echo "Snippet around problematic area in '$html_file':" | tee -a "$ERROR_LOG_FILE"
|
||||
grep -E 'href="[^"#:/]+\.md\.old"' "$html_file" | head -n 5 | tee -a "$ERROR_LOG_FILE" || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Then, replace links without any extension
|
||||
if ! sed -i -E 's|(href=")([^"#:/.]+(/[^"#:/.]+)*)(")|\1\2.html\4|g' "$html_file"; then
|
||||
echo "Error: Failed to adjust extensionless links in '$html_file'." | tee -a "$ERROR_LOG_FILE"
|
||||
echo "Snippet around problematic area in '$html_file':" | tee -a "$ERROR_LOG_FILE"
|
||||
grep -E 'href="[^"#:/.]+"' "$html_file" | head -n 5 | tee -a "$ERROR_LOG_FILE" || true
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Debug: Print a snippet of the HTML file after running sed
|
||||
echo "Snippet after sed in '$html_file':"
|
||||
head -n 5 "$html_file"
|
||||
echo "..."
|
||||
|
||||
# Log the successful conversion
|
||||
echo "$(date +"%Y-%m-%d %H:%M:%S") - Converted '$md_old_file' to '$html_file'." >> "$LOG_FILE"
|
||||
|
||||
echo "Successfully converted '$md_old_file' to '$html_file'."
|
||||
}
|
||||
|
||||
# Function to synchronize Markdown files and relevant assets to TEMP_DIR
|
||||
synchronize_markdown() {
|
||||
echo "Synchronizing Markdown files and assets to '$TEMP_DIR'..."
|
||||
|
||||
# Use rsync to copy only .md, .pdf, and image files, excluding unwanted directories and files
|
||||
rsync -av --delete \
|
||||
--exclude='*.html' \
|
||||
--exclude='*.sh' \
|
||||
--exclude='.git/' \
|
||||
--exclude='.gitignore' \
|
||||
--exclude='*.bak' \
|
||||
--include='*/' \
|
||||
--include='*.md' \
|
||||
--include='*.pdf' \
|
||||
--include='*.png' \
|
||||
--include='*.jpg' \
|
||||
--include='*.jpeg' \
|
||||
--include='*.gif' \
|
||||
--exclude='*' \
|
||||
"$SOURCE_DIR/" "$TEMP_DIR/" | grep '\.md$' || true
|
||||
|
||||
echo "Synchronization completed."
|
||||
}
|
||||
|
||||
# Function to rename .md files to .md.old in TEMP_DIR
|
||||
rename_md_files() {
|
||||
echo "Renaming new or modified .md files to .md.old in '$TEMP_DIR'..."
|
||||
|
||||
# Find all .md files in TEMP_DIR
|
||||
find "$TEMP_DIR" -type f -name '*.md' | while IFS= read -r md_file; do
|
||||
# Determine the corresponding .md.old file
|
||||
md_old_file="${md_file}.old"
|
||||
|
||||
# Determine the source .md file
|
||||
source_md="$SOURCE_DIR/${md_file#$TEMP_DIR/}"
|
||||
|
||||
# Check if the .md.old file exists
|
||||
if [[ ! -f "$md_old_file" ]]; then
|
||||
# New file detected, copy to .md.old
|
||||
cp "$source_md" "$md_old_file"
|
||||
echo "New file detected. Copied '$source_md' to '$md_old_file'."
|
||||
# Convert to HTML
|
||||
convert_md_to_html "$md_old_file" &
|
||||
else
|
||||
# Compare the source .md with the existing .md.old
|
||||
if ! diff -q "$source_md" "$md_old_file" &>/dev/null; then
|
||||
# Files differ, update .md.old and reconvert
|
||||
cp "$source_md" "$md_old_file"
|
||||
echo "Modified file detected. Updated '$md_old_file' with changes from '$source_md'."
|
||||
# Convert to HTML
|
||||
convert_md_to_html "$md_old_file" &
|
||||
else
|
||||
echo "No changes detected for '$source_md'. Skipping conversion."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Wait for all background conversions to finish
|
||||
wait
|
||||
|
||||
echo "Renaming and conversion of new or modified .md files completed."
|
||||
}
|
||||
|
||||
# Function to handle deletions: Remove .html files corresponding to deleted .md files
|
||||
handle_deletions() {
|
||||
echo "Handling deletions of Markdown files..."
|
||||
|
||||
# Find all .md.old files in TEMP_DIR
|
||||
find "$TEMP_DIR" -type f -name '*.md.old' | while IFS= read -r md_old_file; do
|
||||
# Determine the corresponding .md file in SOURCE_DIR
|
||||
source_md="$SOURCE_DIR/${md_old_file#$TEMP_DIR/}"
|
||||
source_md="${source_md%.md.old}.md"
|
||||
|
||||
# Check if the source .md file exists
|
||||
if [[ ! -f "$source_md" ]]; then
|
||||
# Corresponding .md file has been deleted, remove the .html file
|
||||
html_file="${md_old_file%.md.old}.html"
|
||||
if [[ -f "$html_file" ]]; then
|
||||
rm "$html_file"
|
||||
echo "Deleted '$html_file' as the source Markdown file no longer exists."
|
||||
# Log the deletion
|
||||
echo "$(date +"%Y-%m-%d %H:%M:%S") - Deleted '$html_file' due to source removal." >> "$LOG_FILE"
|
||||
fi
|
||||
# Optionally, remove the .md.old file itself
|
||||
rm "$md_old_file"
|
||||
echo "Removed obsolete '$md_old_file'."
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Deletion handling completed."
|
||||
}
|
||||
|
||||
# Function to generate index.html specifically
|
||||
generate_index() {
|
||||
local index_md_old="$TEMP_DIR/index.md.old"
|
||||
local index_html="$TEMP_DIR/index.html"
|
||||
|
||||
if [[ ! -f "$index_md_old" ]]; then
|
||||
echo "Error: 'index.md.old' not found in '$TEMP_DIR'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Generating 'index.html' from 'index.md.old'..."
|
||||
|
||||
# Convert the index.md.old file to HTML
|
||||
convert_md_to_html "$index_md_old"
|
||||
|
||||
# Ensure index.html exists
|
||||
if [[ ! -f "$index_html" ]]; then
|
||||
echo "Error: Failed to generate 'index.html'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "'index.html' generation completed."
|
||||
}
|
||||
|
||||
# Function to open index.html in qutebrowser
|
||||
open_browser() {
|
||||
local index_file="$TEMP_DIR/index.html"
|
||||
if [[ -f "$index_file" ]]; then
|
||||
echo "Opening '$index_file' in qutebrowser..."
|
||||
qutebrowser "$index_file" &
|
||||
echo "Opened '$index_file' in qutebrowser."
|
||||
else
|
||||
echo "Error: '$index_file' does not exist. Please ensure it is generated correctly."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to update synchronization and conversion based on differences
|
||||
update_if_needed() {
|
||||
# Synchronize new and updated files
|
||||
synchronize_markdown
|
||||
# Rename and convert new or modified .md files
|
||||
rename_md_files
|
||||
# Handle deletions
|
||||
handle_deletions
|
||||
# Generate index.html
|
||||
generate_index
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Main Script Execution
|
||||
# ============================
|
||||
|
||||
main() {
|
||||
check_dependencies
|
||||
|
||||
# Create TEMP_DIR if it doesn't exist
|
||||
if [[ ! -d "$TEMP_DIR" ]]; then
|
||||
echo "Temporary directory '$TEMP_DIR' does not exist. Creating and performing full synchronization."
|
||||
mkdir -p "$TEMP_DIR"
|
||||
synchronize_markdown
|
||||
rename_md_files
|
||||
handle_deletions
|
||||
generate_index
|
||||
else
|
||||
echo "Temporary directory '$TEMP_DIR' already exists. Checking for updates."
|
||||
update_if_needed
|
||||
fi
|
||||
|
||||
open_browser
|
||||
|
||||
echo "All tasks completed successfully."
|
||||
}
|
||||
|
||||
main
|
||||
27
nvim/scripts/setup-plugins.sh
Executable file
27
nvim/scripts/setup-plugins.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
PREFIX="$HOME/.config/nvim/pack/nvim/start"
|
||||
mkdir -p "$PREFIX"
|
||||
|
||||
git clone https://github.com/numToStr/Comment.nvim "$PREFIX/comment"
|
||||
git clone https://github.com/ellisonleao/gruvbox.nvim "$PREFIX/gruvbox"
|
||||
git clone https://github.com/nvim-lualine/lualine.nvim "$PREFIX/lualine"
|
||||
git clone https://github.com/vimwiki/vimwiki "$PREFIX/vimwiki"
|
||||
git clone https://github.com/lewis6991/gitsigns.nvim "$PREFIX/gitsigns"
|
||||
git clone https://github.com/hrsh7th/nvim-cmp "$PREFIX/nvim-cmp"
|
||||
git clone https://github.com/hrsh7th/cmp-nvim-lsp "$PREFIX/cmp-nvim-lsp"
|
||||
git clone https://github.com/hrsh7th/cmp-buffer "$PREFIX/cmp-bufffer"
|
||||
git clone https://github.com/hrsh7th/cmp-path "$PREFIX/cmp-path"
|
||||
git clone https://github.com/hrsh7th/cmp-cmdline "$PREFIX/cmp-cmdline"
|
||||
git clone https://github.com/saadparwaiz1/cmp_luasnip "$PREFIX/cmp_luasnip"
|
||||
git clone https://github.com/L3MON4D3/LuaSnip "$PREFIX/luasnip"
|
||||
git clone https://github.com/nvim-treesitter/nvim-treesitter "$PREFIX/nvim-treesitter"
|
||||
git clone https://github.com/windwp/nvim-autopairs "$PREFIX/nvim-autopairs"
|
||||
git clone https://github.com/folke/which-key.nvim "$PREFIX/which-key"
|
||||
git clone https://github.com/nvim-tree/nvim-web-devicons "$PREFIX/nvim-web-devicons"
|
||||
git clone https://github.com/nvim-lua/plenary.nvim "$PREFIX/plenary"
|
||||
git clone https://github.com/nvim-treesitter/nvim-treesitter "$PREFIX/nvim-treesitter"
|
||||
git clone https://github.com/neovim/nvim-lspconfig "$PREFIX/nvim-lspconfig"
|
||||
git clone https://github.com/iamcco/markdown-preview.nvim "$PREFIX/markdown-preview"
|
||||
git clone https://github.com/nvim-telescope/telescope.nvim "$PREFIX/telescope"
|
||||
sudo apt install ripgrep
|
||||
654
nvim/scripts/test.readme.md
Normal file
654
nvim/scripts/test.readme.md
Normal file
@@ -0,0 +1,654 @@
|
||||
|
||||
## **6. Improving the `extract_title` Function**
|
||||
|
||||
### **a. Handling Complex YAML Frontmatter**
|
||||
|
||||
**Issue:** YAML frontmatter can contain more complex structures, including quotes, multiple spaces, or special characters, which the current `extract_title` function might not handle correctly.
|
||||
|
||||
**Solution:** Enhance the title extraction to handle various YAML frontmatter formats more robustly.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```bash
|
||||
extract_title() {
|
||||
local md_file="$1"
|
||||
# Extract title from YAML frontmatter, handling quotes and spaces
|
||||
local title
|
||||
title=$(awk '/^title:/ {
|
||||
sub(/^title:\s*/, "");
|
||||
gsub(/['"'"']/,"", $0);
|
||||
print
|
||||
exit
|
||||
}' "$md_file") || true
|
||||
if [[ -z "$title" ]]; then
|
||||
# Fallback to filename without extension
|
||||
title=$(basename "$md_file" .md.old)
|
||||
fi
|
||||
echo "$title"
|
||||
}
|
||||
```
|
||||
|
||||
**Explanation:**
|
||||
|
||||
- **`awk` Usage:**
|
||||
- Searches for a line starting with `title:`.
|
||||
- Removes the `title:` prefix and any leading whitespace.
|
||||
- Removes surrounding quotes if present.
|
||||
- Prints the title and exits to prevent processing the entire file.
|
||||
|
||||
---
|
||||
|
||||
## **10. Incorporating Configuration Flexibility**
|
||||
|
||||
### **Issue:**
|
||||
Hardcoding paths and settings reduces the script's flexibility and adaptability to different environments.
|
||||
|
||||
### **Solution:**
|
||||
Allow configuration through external files or command-line arguments, making the script more versatile.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
1. **Using a Configuration File:**
|
||||
|
||||
- **Create a Config File (`config.sh`):**
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
SOURCE_DIR="$HOME/vimwiki"
|
||||
TEMP_DIR="/tmp/vimwikihtml"
|
||||
CSS_FILE="$HOME/.local/share/nvim/style.css"
|
||||
CONCURRENT_JOBS=4
|
||||
LOG_FILE="$TEMP_DIR/conversion.log"
|
||||
ERROR_LOG_FILE="$TEMP_DIR/error.log"
|
||||
```
|
||||
|
||||
- **Source the Config File in the Script:**
|
||||
|
||||
```bash
|
||||
# At the beginning of test.sh
|
||||
if [[ -f "$HOME/vimwiki/config.sh" ]]; then
|
||||
source "$HOME/vimwiki/config.sh"
|
||||
else
|
||||
echo "Error: Configuration file 'config.sh' not found in '$HOME/vimwiki/'."
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
2. **Using Command-Line Arguments:**
|
||||
|
||||
- **Parse Arguments Using `getopts`:**
|
||||
|
||||
```bash
|
||||
while getopts "s:t:c:j:l:e:" opt; do
|
||||
case $opt in
|
||||
s) SOURCE_DIR="$OPTARG" ;;
|
||||
t) TEMP_DIR="$OPTARG" ;;
|
||||
c) CSS_FILE="$OPTARG" ;;
|
||||
j) CONCURRENT_JOBS="$OPTARG" ;;
|
||||
l) LOG_FILE="$OPTARG" ;;
|
||||
e) ERROR_LOG_FILE="$OPTARG" ;;
|
||||
*) echo "Invalid option"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
```
|
||||
|
||||
- **Usage Example:**
|
||||
|
||||
```bash
|
||||
./test.sh -s "/path/to/source" -t "/path/to/temp" -c "/path/to/style.css" -j 8
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- **Flexibility:** Easily adapt to different directories, styles, and settings without modifying the script.
|
||||
- **Reusability:** Share the script across different projects with varying configurations.
|
||||
|
||||
---
|
||||
|
||||
## **11. Adding a Dry-Run Mode for Safe Testing**
|
||||
|
||||
### **Issue:**
|
||||
Making changes to files (like deletions) without a preview can be risky, especially during testing.
|
||||
|
||||
### **Solution:**
|
||||
Implement a dry-run mode that simulates actions without performing them, allowing you to verify behavior before actual execution.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
1. **Introduce a `DRY_RUN` Variable:**
|
||||
|
||||
```bash
|
||||
DRY_RUN=false
|
||||
```
|
||||
|
||||
2. **Parse a Command-Line Argument for Dry-Run:**
|
||||
|
||||
```bash
|
||||
while getopts "s:t:c:j:l:e:dr" opt; do
|
||||
case $opt in
|
||||
# ... [existing options]
|
||||
d) DRY_RUN=true ;;
|
||||
r) # Any other options
|
||||
;;
|
||||
*) echo "Invalid option"; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
```
|
||||
|
||||
3. **Modify File Operations to Respect Dry-Run:**
|
||||
|
||||
```bash
|
||||
handle_deletions() {
|
||||
echo "Handling deletions of Markdown files..."
|
||||
|
||||
find "$TEMP_DIR" -type f -name '*.md.old' -print0 | while IFS= read -r -d '' md_old_file; do
|
||||
source_md="$SOURCE_DIR/${md_old_file#$TEMP_DIR/}"
|
||||
source_md="${source_md%.md.old}.md"
|
||||
|
||||
if [[ ! -f "$source_md" ]]; then
|
||||
html_file="${md_old_file%.md.old}.html"
|
||||
|
||||
if [[ "$DRY_RUN" = true ]]; then
|
||||
echo "Dry-Run: Would delete '$html_file' as the source Markdown file no longer exists."
|
||||
else
|
||||
if [[ -f "$html_file" ]]; then
|
||||
rm "$html_file"
|
||||
echo "Deleted '$html_file' as the source Markdown file no longer exists."
|
||||
echo "$(date +"%Y-%m-%d %H:%M:%S") - Deleted '$html_file' due to source removal." >> "$LOG_FILE"
|
||||
fi
|
||||
|
||||
rm "$md_old_file"
|
||||
echo "Removed obsolete '$md_old_file'."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Deletion handling completed."
|
||||
}
|
||||
```
|
||||
|
||||
4. **Inform Users When in Dry-Run Mode:**
|
||||
|
||||
```bash
|
||||
if [[ "$DRY_RUN" = true ]]; then
|
||||
echo "Running in Dry-Run mode. No changes will be made."
|
||||
fi
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- **Safety:** Allows you to verify what actions the script would take without making actual changes.
|
||||
- **Testing:** Facilitates safe testing and debugging of the script's logic.
|
||||
|
||||
---
|
||||
|
||||
|
||||
**Explanation:**
|
||||
|
||||
- **`trap cleanup SIGINT SIGTERM`:** Catches interrupt (`Ctrl+C`) and termination signals, invoking the `cleanup` function.
|
||||
- **`cleanup` Function:**
|
||||
- Echoes a message indicating interruption.
|
||||
- Terminates all background jobs to prevent orphan processes.
|
||||
- Exits the script with a non-zero status.
|
||||
|
||||
---
|
||||
|
||||
## **13. Providing User Feedback and Progress Indicators**
|
||||
|
||||
### **Issue:**
|
||||
With large numbers of files, users might find it hard to gauge progress or know if the script is still running.
|
||||
|
||||
### **Solution:**
|
||||
Implement progress indicators or verbose outputs to inform users about the script's status.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
1. **Using Progress Bars with `pv`:**
|
||||
|
||||
**Install `pv`:**
|
||||
|
||||
```bash
|
||||
sudo apt-get install pv
|
||||
```
|
||||
|
||||
2. **Integrate `pv` into File Processing:**
|
||||
|
||||
```bash
|
||||
convert_all_md_to_html() {
|
||||
echo "Starting conversion of updated Markdown files to HTML..."
|
||||
|
||||
find "$TEMP_DIR" -type f -name '*.md.old' -print0 | pv -0 -l -s "$(find "$TEMP_DIR" -type f -name '*.md.old' | wc -l)" | parallel -0 -j "$CONCURRENT_JOBS" convert_md_to_html {}
|
||||
|
||||
echo "Markdown to HTML conversion completed."
|
||||
}
|
||||
```
|
||||
|
||||
3. **Simple Progress Indicators:**
|
||||
|
||||
Alternatively, use echo statements to indicate progress.
|
||||
|
||||
```bash
|
||||
convert_md_to_html() {
|
||||
local md_old_file="$1"
|
||||
echo "Processing file: $md_old_file"
|
||||
# ... [rest of the function]
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- **User Awareness:** Users are informed about the script's progress, enhancing usability.
|
||||
- **Debugging:** Progress indicators can help identify if the script is stuck or processing unusually.
|
||||
|
||||
---
|
||||
|
||||
## **14. Adding a Help Message and Usage Instructions**
|
||||
|
||||
### **Issue:**
|
||||
Users might not be aware of available options or how to use the script effectively.
|
||||
|
||||
### **Solution:**
|
||||
Implement a help message that outlines usage instructions and available options.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```bash
|
||||
print_help() {
|
||||
echo "Usage: $0 [options]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " -s DIR Source Vimwiki directory (default: \$HOME/vimwiki)"
|
||||
echo " -t DIR Temporary HTML output directory (default: /tmp/vimwikihtml)"
|
||||
echo " -c FILE Path to the CSS file for styling (default: \$HOME/.local/share/nvim/style.css)"
|
||||
echo " -j NUM Number of concurrent pandoc processes (default: 4)"
|
||||
echo " -l FILE Log file to track conversions (default: /tmp/vimwikihtml/conversion.log)"
|
||||
echo " -e FILE Log file to track errors (default: /tmp/vimwikihtml/error.log)"
|
||||
echo " -d Enable Dry-Run mode"
|
||||
echo " -h Display this help message"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0 -s ~/myvimwiki -t ~/htmloutput -c ~/styles/style.css -j 8"
|
||||
}
|
||||
```
|
||||
|
||||
Add argument parsing to handle the `-h` option:
|
||||
|
||||
```bash
|
||||
# Parse command-line arguments
|
||||
while getopts "s:t:c:j:l:e:dh" opt; do
|
||||
case $opt in
|
||||
s) SOURCE_DIR="$OPTARG" ;;
|
||||
t) TEMP_DIR="$OPTARG" ;;
|
||||
c) CSS_FILE="$OPTARG" ;;
|
||||
j) CONCURRENT_JOBS="$OPTARG" ;;
|
||||
l) LOG_FILE="$OPTARG" ;;
|
||||
e) ERROR_LOG_FILE="$OPTARG" ;;
|
||||
d) DRY_RUN=true ;;
|
||||
h)
|
||||
print_help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option."
|
||||
print_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- **Usability:** Makes the script more user-friendly and accessible.
|
||||
- **Documentation:** Provides immediate reference without external documentation.
|
||||
|
||||
---
|
||||
|
||||
## **17. Adding Verbose and Quiet Modes**
|
||||
|
||||
### **Issue:**
|
||||
Sometimes users might prefer minimal output, especially when running the script in automated environments, while other times they might want detailed logs.
|
||||
|
||||
### **Solution:**
|
||||
Implement verbose (`-v`) and quiet (`-q`) modes to control the script's output.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
1. **Introduce Variables:**
|
||||
|
||||
```bash
|
||||
VERBOSE=false
|
||||
QUIET=false
|
||||
```
|
||||
|
||||
2. **Update Argument Parsing:**
|
||||
|
||||
```bash
|
||||
while getopts "s:t:c:j:l:e:dhvq" opt; do
|
||||
case $opt in
|
||||
# ... [existing options]
|
||||
v) VERBOSE=true ;;
|
||||
q) QUIET=true ;;
|
||||
# ... [other options]
|
||||
esac
|
||||
done
|
||||
```
|
||||
|
||||
3. **Modify Echo Statements:**
|
||||
|
||||
```bash
|
||||
log() {
|
||||
if [[ "$VERBOSE" = true && "$QUIET" = false ]]; then
|
||||
echo "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Replace echo with log where appropriate
|
||||
log "Starting conversion of '$md_old_file'..."
|
||||
```
|
||||
|
||||
4. **Suppress Non-Essential Output in Quiet Mode:**
|
||||
|
||||
```bash
|
||||
if [[ "$QUIET" = true ]]; then
|
||||
exec >/dev/null 2>&1
|
||||
fi
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- **Flexibility:** Users can choose the level of output based on their needs.
|
||||
- **Usability:** Enhances the script's adaptability in different contexts.
|
||||
|
||||
---
|
||||
|
||||
## **18. Documentation and In-Line Comments**
|
||||
|
||||
### **Issue:**
|
||||
As scripts grow in complexity, maintaining clear documentation and comments becomes essential for future maintenance and collaboration.
|
||||
|
||||
### **Solution:**
|
||||
Add comprehensive in-line comments explaining the purpose and functionality of code sections and functions.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```bash
|
||||
# Function to convert a single Markdown file to HTML
|
||||
# Arguments:
|
||||
# $1 - Path to the .md.old file
|
||||
convert_md_to_html() {
|
||||
# ... [function code]
|
||||
}
|
||||
```
|
||||
|
||||
**Recommendation:** Regularly update comments to reflect any changes in the script's logic or functionality.
|
||||
|
||||
---
|
||||
|
||||
## **19. Testing and Validation**
|
||||
|
||||
### **Issue:**
|
||||
Ensuring that the script behaves as expected in various scenarios is crucial for reliability.
|
||||
|
||||
### **Solution:**
|
||||
Implement automated tests or conduct thorough manual testing covering different cases like:
|
||||
|
||||
- Adding new files.
|
||||
- Modifying existing files.
|
||||
- Deleting files.
|
||||
- Handling files with special characters.
|
||||
- Running in dry-run mode.
|
||||
- Handling permission issues.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
1. **Automated Testing:**
|
||||
|
||||
- **Use Bash Unit Testing Frameworks:** Tools like [Bats](https://github.com/bats-core/bats-core) can help write automated tests for your script.
|
||||
|
||||
- **Example Test Case:**
|
||||
|
||||
```bash
|
||||
@test "Convert new Markdown file to HTML" {
|
||||
# Setup: Create a new Markdown file in SOURCE_DIR
|
||||
echo "# Test Page" > "$SOURCE_DIR/test.md"
|
||||
|
||||
# Run the script
|
||||
~/vimwiki/test.sh
|
||||
|
||||
# Assert: Check if HTML file exists
|
||||
[ -f "$TEMP_DIR/test.html" ]
|
||||
|
||||
# Cleanup
|
||||
rm "$SOURCE_DIR/test.md" "$TEMP_DIR/test.html" "$TEMP_DIR/test.md.old"
|
||||
}
|
||||
```
|
||||
|
||||
2. **Manual Testing:**
|
||||
|
||||
- **Create Test Files:** Set up various Markdown files with different content and filenames.
|
||||
- **Run the Script:** Execute the script and verify the HTML output.
|
||||
- **Check Logs:** Ensure that logs accurately reflect the actions taken.
|
||||
- **Edge Cases:** Test with empty files, very large files, and files with complex YAML frontmatter.
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- **Reliability:** Ensures that the script performs correctly under various conditions.
|
||||
- **Confidence:** Builds trust in the script's functionality before deploying it in critical environments.
|
||||
|
||||
---
|
||||
|
||||
## **20. Additional Feature Suggestions**
|
||||
|
||||
### **a. Watch Mode for Real-Time Updates**
|
||||
|
||||
**Issue:**
|
||||
Manually running the script after every change can be tedious.
|
||||
|
||||
**Solution:**
|
||||
Implement a watch mode that monitors the source directory for changes and triggers the script automatically.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
1. **Use `inotifywait`:**
|
||||
|
||||
**Install `inotify-tools`:**
|
||||
|
||||
```bash
|
||||
sudo apt-get install inotify-tools
|
||||
```
|
||||
|
||||
2. **Add a Watch Mode Option:**
|
||||
|
||||
```bash
|
||||
WATCH_MODE=false
|
||||
|
||||
while getopts "s:t:c:j:l:e:dhvqw" opt; do
|
||||
case $opt in
|
||||
# ... [existing options]
|
||||
w) WATCH_MODE=true ;;
|
||||
# ... [other options]
|
||||
esac
|
||||
done
|
||||
```
|
||||
|
||||
3. **Implement Watch Mode in the Script:**
|
||||
|
||||
```bash
|
||||
watch_mode() {
|
||||
echo "Entering Watch Mode. Monitoring '$SOURCE_DIR' for changes..."
|
||||
while inotifywait -r -e modify,create,delete,move "$SOURCE_DIR"; do
|
||||
echo "Change detected. Updating Vimwiki HTML..."
|
||||
update_if_needed
|
||||
open_browser
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
check_dependencies
|
||||
|
||||
if [[ "$WATCH_MODE" = true ]]; then
|
||||
# Initial synchronization
|
||||
if [[ ! -d "$TEMP_DIR" ]]; then
|
||||
mkdir -p "$TEMP_DIR"
|
||||
synchronize_markdown
|
||||
rename_md_files
|
||||
handle_deletions
|
||||
generate_index
|
||||
else
|
||||
update_if_needed
|
||||
fi
|
||||
|
||||
# Enter watch mode
|
||||
watch_mode
|
||||
else
|
||||
# Regular execution
|
||||
if [[ ! -d "$TEMP_DIR" ]]; then
|
||||
echo "Temporary directory '$TEMP_DIR' does not exist. Creating and performing full synchronization."
|
||||
mkdir -p "$TEMP_DIR"
|
||||
synchronize_markdown
|
||||
rename_md_files
|
||||
handle_deletions
|
||||
generate_index
|
||||
else
|
||||
echo "Temporary directory '$TEMP_DIR' already exists. Checking for updates."
|
||||
update_if_needed
|
||||
fi
|
||||
|
||||
open_browser
|
||||
|
||||
echo "All tasks completed successfully."
|
||||
fi
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- **Convenience:** Automatically keeps the HTML output up-to-date in real-time.
|
||||
- **Efficiency:** Eliminates the need for manual script execution after each change.
|
||||
|
||||
**Note:** Watch mode can consume more resources. Use it judiciously based on your system's capabilities.
|
||||
|
||||
---
|
||||
|
||||
## **22. Implementing a Configuration Validation Step**
|
||||
|
||||
### **Issue:**
|
||||
Incorrect configurations (like wrong directory paths) can lead to script failures or unintended behavior.
|
||||
|
||||
### **Solution:**
|
||||
Validate configuration parameters at the beginning of the script to ensure they are correct.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```bash
|
||||
validate_config() {
|
||||
# Validate SOURCE_DIR
|
||||
if [[ ! -d "$SOURCE_DIR" ]]; then
|
||||
handle_error "Source directory '$SOURCE_DIR' does not exist."
|
||||
exit "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
# Validate CSS_FILE
|
||||
if [[ ! -f "$CSS_FILE" ]]; then
|
||||
echo "Warning: CSS file '$CSS_FILE' does not exist. HTML files will be generated without styling."
|
||||
fi
|
||||
|
||||
# Validate TEMP_DIR (already handled in main)
|
||||
}
|
||||
|
||||
main() {
|
||||
check_dependencies
|
||||
validate_config
|
||||
|
||||
# ... [rest of the main function]
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- **Prevention:** Catches configuration issues early, preventing the script from running with invalid settings.
|
||||
- **User Guidance:** Provides clear error messages to help users correct configurations.
|
||||
|
||||
---
|
||||
|
||||
## **23. Utilizing `set -x` for Debugging**
|
||||
|
||||
### **Issue:**
|
||||
Debugging complex scripts can be challenging without visibility into their execution flow.
|
||||
|
||||
### **Solution:**
|
||||
Use Bash's debugging options to trace the script's execution when needed.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
1. **Add a Verbose Flag (`-x`):**
|
||||
|
||||
```bash
|
||||
set -euo pipefail
|
||||
[[ "$VERBOSE" = true ]] && set -x
|
||||
```
|
||||
|
||||
2. **Enable Verbose Mode via Command-Line Argument:**
|
||||
|
||||
```bash
|
||||
while getopts "s:t:c:j:l:e:dhvqx" opt; do
|
||||
case $opt in
|
||||
# ... [existing options]
|
||||
x) VERBOSE=true ;;
|
||||
# ... [other options]
|
||||
esac
|
||||
done
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- **Transparency:** Provides detailed execution logs for debugging purposes.
|
||||
- **Control:** Users can enable or disable debugging as needed without modifying the script.
|
||||
|
||||
**Caution:** Ensure that verbose mode does not expose sensitive information, especially when handling logs.
|
||||
|
||||
---
|
||||
|
||||
## **24. Preventing Infinite Loops or Excessive Resource Consumption**
|
||||
|
||||
### **Issue:**
|
||||
If the script triggers file changes (like converting `.md.old` to `.html`), it might inadvertently cause itself to detect changes, leading to infinite loops or excessive resource usage.
|
||||
|
||||
### **Solution:**
|
||||
Ensure that the script only processes intended file types and excludes its own output from triggering further actions.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
1. **Exclude `.html` and `.md.old` Files from Synchronization:**
|
||||
|
||||
- Already handled via `rsync` excludes.
|
||||
|
||||
2. **Ensure `handle_deletions` and Other Functions Do Not Modify Source Directory:**
|
||||
|
||||
- All file operations target the temporary directory, ensuring the source remains untouched.
|
||||
|
||||
3. **Double-Check `rsync` Include/Exclude Patterns:**
|
||||
|
||||
```bash
|
||||
rsync -av --delete \
|
||||
--exclude='*.html' \
|
||||
--exclude='*.sh' \
|
||||
--exclude='.git/' \
|
||||
--exclude='.gitignore' \
|
||||
--exclude='*.bak' \
|
||||
--include='*/' \
|
||||
--include='*.md' \
|
||||
--include='*.pdf' \
|
||||
--include='*.png' \
|
||||
--include='*.jpg' \
|
||||
--include='*.jpeg' \
|
||||
--include='*.gif' \
|
||||
--exclude='*' \
|
||||
"$SOURCE_DIR/" "$TEMP_DIR/"
|
||||
```
|
||||
|
||||
**Recommendation:** Regularly review and test the script to ensure it doesn't inadvertently process or modify unintended files.
|
||||
|
||||
---
|
||||
435
nvim/scripts/test.sh.bak
Executable file
435
nvim/scripts/test.sh.bak
Executable file
@@ -0,0 +1,435 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# ============================
|
||||
# Exit Status Codes
|
||||
# ============================
|
||||
EXIT_SUCCESS=0
|
||||
EXIT_FAILURE=1
|
||||
EXIT_DEPENDENCY=2
|
||||
EXIT_CONVERSION=3
|
||||
|
||||
# ============================
|
||||
# Shell Options
|
||||
# ============================
|
||||
# Exit immediately if a command exits with a non-zero status,
|
||||
# Treat unset variables as an error,
|
||||
# Prevent errors in a pipeline from being masked
|
||||
set -euo pipefail
|
||||
|
||||
# ============================
|
||||
# Color Definitions
|
||||
# ============================
|
||||
RESET='\033[0m' # No Color
|
||||
BOLD='\033[1m'
|
||||
|
||||
# Regular Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
|
||||
# ============================
|
||||
# Configuration Constants
|
||||
# ============================
|
||||
SOURCE_DIR="$HOME/vimwiki" # Source Vimwiki directory
|
||||
TEMP_DIR="/tmp/vimwikihtml" # Temporary HTML output directory
|
||||
CSS_FILE="$HOME/.local/share/nvim/style.css" # Path to the CSS file for styling
|
||||
CONCURRENT_JOBS=4 # Number of concurrent pandoc processes
|
||||
LOG_FILE="$TEMP_DIR/conversion.log" # Log file to track conversions
|
||||
ERROR_LOG_FILE="$TEMP_DIR/error.log" # Log file to track errors
|
||||
|
||||
# ============================
|
||||
# Dependencies
|
||||
# ============================
|
||||
DEPENDENCIES=("pandoc" "sed" "qutebrowser" "find" "rsync" "grep" "mkdir" "basename" "diff")
|
||||
|
||||
# ============================
|
||||
# Logging Functions
|
||||
# ============================
|
||||
|
||||
# Function to log INFO messages
|
||||
log_info() {
|
||||
local message="$1"
|
||||
echo -e "${BLUE}[INFO]${RESET} $message"
|
||||
echo "[INFO] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to log SUCCESS messages
|
||||
log_success() {
|
||||
local message="$1"
|
||||
echo -e "${GREEN}[SUCCESS]${RESET} $message"
|
||||
echo "[SUCCESS] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to log WARNING messages
|
||||
log_warning() {
|
||||
local message="$1"
|
||||
echo -e "${YELLOW}[WARNING]${RESET} $message"
|
||||
echo "[WARNING] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to log ERROR messages
|
||||
log_error() {
|
||||
local message="$1"
|
||||
echo -e "${RED}[ERROR]${RESET} $message" | tee -a "$ERROR_LOG_FILE"
|
||||
echo "[ERROR] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$ERROR_LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to handle errors with context and exit with specific code
|
||||
handle_error() {
|
||||
local message="$1"
|
||||
local exit_code="${2:-$EXIT_FAILURE}"
|
||||
local func="${FUNCNAME[1]}"
|
||||
local line="${BASH_LINENO[0]}"
|
||||
log_error "In function '$func' at line $line: $message"
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Filename Validation Function
|
||||
# ============================
|
||||
|
||||
# Function to check if a filename contains only allowed characters
|
||||
is_valid_filename() {
|
||||
local filename="$1"
|
||||
if [[ "$filename" =~ ^[A-Za-z0-9._-]+$ ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Bash Availability Check
|
||||
# ============================
|
||||
|
||||
# Function to ensure that Bash is available
|
||||
check_bash() {
|
||||
if ! command -v bash &>/dev/null; then
|
||||
echo -e "${RED}[ERROR]${RESET} Bash is not installed. Please install Bash to run this script."
|
||||
exit "$EXIT_FAILURE"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Path Validation Function
|
||||
# ============================
|
||||
|
||||
# Function to validate and sanitize input paths
|
||||
validate_paths() {
|
||||
log_info "Validating input paths..."
|
||||
|
||||
# Check that SOURCE_DIR is an absolute path
|
||||
if [[ "$SOURCE_DIR" != /* ]]; then
|
||||
handle_error "SOURCE_DIR ('$SOURCE_DIR') is not an absolute path." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
# Check that TEMP_DIR is an absolute path and under /tmp
|
||||
if [[ "$TEMP_DIR" != /tmp/* ]]; then
|
||||
handle_error "TEMP_DIR ('$TEMP_DIR') must be under /tmp." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
# Check that SOURCE_DIR exists and is a directory
|
||||
if [[ ! -d "$SOURCE_DIR" ]]; then
|
||||
handle_error "SOURCE_DIR ('$SOURCE_DIR') does not exist or is not a directory." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
# Check if TEMP_DIR exists; if not, it will be created later
|
||||
# If it exists, ensure it's a directory
|
||||
if [[ -e "$TEMP_DIR" && ! -d "$TEMP_DIR" ]]; then
|
||||
handle_error "TEMP_DIR ('$TEMP_DIR') exists but is not a directory." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
log_success "Input paths are valid."
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Function Definitions
|
||||
# ============================
|
||||
|
||||
# Function to check if all dependencies are installed
|
||||
check_dependencies() {
|
||||
log_info "Checking for required dependencies..."
|
||||
local missing_dependencies=()
|
||||
|
||||
for cmd in "${DEPENDENCIES[@]}"; do
|
||||
if ! command -v "$cmd" &>/dev/null; then
|
||||
missing_dependencies+=("$cmd")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#missing_dependencies[@]} -ne 0 ]]; then
|
||||
for cmd in "${missing_dependencies[@]}"; do
|
||||
handle_error "Dependency '$cmd' is not installed. Please install it and retry." "$EXIT_DEPENDENCY"
|
||||
done
|
||||
fi
|
||||
|
||||
log_success "All dependencies are satisfied."
|
||||
}
|
||||
|
||||
# Function to extract the title from Markdown using YAML frontmatter or fallback to filename
|
||||
extract_title() {
|
||||
local md_file="$1"
|
||||
# Attempt to extract title from YAML frontmatter
|
||||
local title
|
||||
title=$(grep -m1 '^title:' "$md_file" | sed 's/title: //') || true
|
||||
if [[ -z "$title" ]]; then
|
||||
# If no title found, use the filename without extension
|
||||
title=$(basename "$md_file" .md.old)
|
||||
fi
|
||||
echo "$title"
|
||||
}
|
||||
|
||||
# Function to convert a single Markdown file to HTML atomically
|
||||
convert_md_to_html() {
|
||||
local md_old_file="$1" # Path to the .md.old file
|
||||
# Determine the relative path from TEMP_DIR
|
||||
local relative_path="${md_old_file#$TEMP_DIR/}"
|
||||
# Remove .md.old extension
|
||||
relative_path="${relative_path%.md.old}"
|
||||
# Determine the output HTML file path
|
||||
local html_file="$TEMP_DIR/${relative_path}.html"
|
||||
# Determine the temporary HTML file path
|
||||
local temp_html_file="${html_file}.tmp"
|
||||
# Create the necessary directories for the HTML file
|
||||
mkdir -p "$(dirname "$html_file")"
|
||||
|
||||
# Extract the title for the HTML document
|
||||
local title
|
||||
title=$(extract_title "$md_old_file")
|
||||
|
||||
log_info "Converting '$md_old_file' to '$html_file'..."
|
||||
|
||||
# Use pandoc to convert Markdown to HTML with CSS and metadata
|
||||
if [[ -f "$CSS_FILE" ]]; then
|
||||
if ! pandoc -f markdown -s --css="$CSS_FILE" --metadata title="$title" "$md_old_file" -o "$temp_html_file"; then
|
||||
handle_error "Failed to convert '$md_old_file' to HTML." "$EXIT_CONVERSION"
|
||||
fi
|
||||
else
|
||||
log_warning "CSS file '$CSS_FILE' not found. Skipping CSS for '$md_old_file'."
|
||||
if ! pandoc -f markdown -s --metadata title="$title" "$md_old_file" -o "$temp_html_file"; then
|
||||
handle_error "Failed to convert '$md_old_file' to HTML." "$EXIT_CONVERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Debug: Print a snippet of the HTML file before running sed
|
||||
log_info "Snippet before sed in '$temp_html_file':"
|
||||
head -n 5 "$temp_html_file" || true
|
||||
echo "..."
|
||||
|
||||
# Adjust internal href links:
|
||||
# 1. Replace href="path/to/file.md.old" with href="path/to/file.html"
|
||||
# 2. Replace href="path/to/file" with href="path/to/file.html" only if 'file' has no extension
|
||||
log_info "Adjusting links in '$html_file'..."
|
||||
|
||||
# First, replace links ending with .md.old
|
||||
if ! sed -i -E 's|(href=")([^"#:/]+(/[^"#:/]+)*)\.md\.old(")|\1\2.html\4|g' "$temp_html_file"; then
|
||||
handle_error "Failed to adjust '.md.old' links in '$temp_html_file'." "$EXIT_CONVERSION"
|
||||
fi
|
||||
|
||||
# Then, replace links without any extension
|
||||
if ! sed -i -E 's|(href=")([^"#:/.]+(/[^"#:/.]+)*)(")|\1\2.html\4|g' "$temp_html_file"; then
|
||||
handle_error "Failed to adjust extensionless links in '$temp_html_file'." "$EXIT_CONVERSION"
|
||||
fi
|
||||
|
||||
# Move the temporary HTML file to the final destination atomically
|
||||
mv "$temp_html_file" "$html_file"
|
||||
|
||||
# Debug: Print a snippet of the HTML file after running sed
|
||||
log_info "Snippet after sed in '$html_file':"
|
||||
head -n 5 "$html_file" || true
|
||||
echo "..."
|
||||
|
||||
# Log the successful conversion
|
||||
echo "$(date +"%Y-%m-%d %H:%M:%S") - Converted '$md_old_file' to '$html_file'." >> "$LOG_FILE"
|
||||
|
||||
log_success "Successfully converted '$md_old_file' to '$html_file'."
|
||||
}
|
||||
|
||||
# Function to synchronize Markdown files and relevant assets to TEMP_DIR
|
||||
synchronize_markdown() {
|
||||
log_info "Synchronizing Markdown files and assets to '$TEMP_DIR'..."
|
||||
|
||||
# Use rsync to copy only .md, .pdf, and image files, excluding unwanted directories and files
|
||||
rsync -av --delete \
|
||||
--exclude='*.html' \
|
||||
--exclude='*.sh' \
|
||||
--exclude='.git/' \
|
||||
--exclude='.gitignore' \
|
||||
--exclude='*.bak' \
|
||||
--include='*/' \
|
||||
--include='*.md' \
|
||||
--include='*.pdf' \
|
||||
--include='*.png' \
|
||||
--include='*.jpg' \
|
||||
--include='*.jpeg' \
|
||||
--include='*.gif' \
|
||||
--exclude='*' \
|
||||
"$SOURCE_DIR/" "$TEMP_DIR/" | grep '\.md$' || true
|
||||
|
||||
log_success "Synchronization completed."
|
||||
}
|
||||
|
||||
# Function to rename .md files to .md.old in TEMP_DIR
|
||||
rename_md_files() {
|
||||
log_info "Renaming new or modified .md files to .md.old in '$TEMP_DIR'..."
|
||||
|
||||
# Find all .md files in TEMP_DIR
|
||||
find "$TEMP_DIR" -type f -name '*.md' | while IFS= read -r md_file; do
|
||||
# Determine the corresponding .md.old file
|
||||
md_old_file="${md_file}.old"
|
||||
|
||||
# Determine the source .md file
|
||||
source_md="$SOURCE_DIR/${md_file#$TEMP_DIR/}"
|
||||
|
||||
# Check if the .md.old file exists
|
||||
if [[ ! -f "$md_old_file" ]]; then
|
||||
# New file detected, copy to .md.old
|
||||
cp "$source_md" "$md_old_file"
|
||||
log_info "New file detected. Copied '$source_md' to '$md_old_file'."
|
||||
# Convert to HTML
|
||||
convert_md_to_html "$md_old_file" &
|
||||
else
|
||||
# Compare the source .md with the existing .md.old
|
||||
if ! diff -q "$source_md" "$md_old_file" &>/dev/null; then
|
||||
# Files differ, update .md.old and reconvert
|
||||
cp "$source_md" "$md_old_file"
|
||||
log_info "Modified file detected. Updated '$md_old_file' with changes from '$source_md'."
|
||||
# Convert to HTML
|
||||
convert_md_to_html "$md_old_file" &
|
||||
else
|
||||
log_info "No changes detected for '$source_md'. Skipping conversion."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Wait for all background conversions to finish
|
||||
wait
|
||||
|
||||
log_success "Renaming and conversion of new or modified .md files completed."
|
||||
}
|
||||
|
||||
# Function to handle deletions: Remove .html files corresponding to deleted .md files
|
||||
handle_deletions() {
|
||||
log_info "Handling deletions of Markdown files..."
|
||||
|
||||
# Find all .md.old files in TEMP_DIR
|
||||
find "$TEMP_DIR" -type f -name '*.md.old' | while IFS= read -r md_old_file; do
|
||||
# Determine the corresponding .md file in SOURCE_DIR
|
||||
source_md="$SOURCE_DIR/${md_old_file#$TEMP_DIR/}"
|
||||
source_md="${source_md%.md.old}.md"
|
||||
|
||||
# Check if the source .md file exists
|
||||
if [[ ! -f "$source_md" ]]; then
|
||||
# Corresponding .md file has been deleted, remove the .html file
|
||||
html_file="${md_old_file%.md.old}.html"
|
||||
if [[ -f "$html_file" ]]; then
|
||||
rm "$html_file"
|
||||
log_success "Deleted '$html_file' as the source Markdown file no longer exists."
|
||||
# Log the deletion
|
||||
echo "$(date +"%Y-%m-%d %H:%M:%S") - Deleted '$html_file' due to source removal." >> "$LOG_FILE"
|
||||
fi
|
||||
# Optionally, remove the .md.old file itself
|
||||
rm "$md_old_file"
|
||||
log_info "Removed obsolete '$md_old_file'."
|
||||
fi
|
||||
done
|
||||
|
||||
log_success "Deletion handling completed."
|
||||
}
|
||||
|
||||
# Function to generate index.html specifically
|
||||
generate_index() {
|
||||
local index_md_old="$TEMP_DIR/index.md.old"
|
||||
local index_html="$TEMP_DIR/index.html"
|
||||
|
||||
if [[ ! -f "$index_md_old" ]]; then
|
||||
handle_error "'index.md.old' not found in '$TEMP_DIR'." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
log_info "Generating 'index.html' from 'index.md.old'..."
|
||||
|
||||
# Convert the index.md.old file to HTML
|
||||
convert_md_to_html "$index_md_old"
|
||||
|
||||
# Ensure index.html exists
|
||||
if [[ ! -f "$index_html" ]]; then
|
||||
handle_error "Failed to generate 'index.html'." "$EXIT_CONVERSION"
|
||||
fi
|
||||
|
||||
log_success "'index.html' generation completed."
|
||||
}
|
||||
|
||||
# Function to open index.html in qutebrowser
|
||||
open_browser() {
|
||||
local index_file="$TEMP_DIR/index.html"
|
||||
if [[ -f "$index_file" ]]; then
|
||||
log_info "Opening '$index_file' in qutebrowser..."
|
||||
qutebrowser "$index_file" &
|
||||
log_success "Opened '$index_file' in qutebrowser."
|
||||
else
|
||||
handle_error "'$index_file' does not exist. Please ensure it is generated correctly." "$EXIT_FAILURE"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to update synchronization and conversion based on differences
|
||||
update_if_needed() {
|
||||
# Synchronize new and updated files
|
||||
synchronize_markdown
|
||||
# Rename and convert new or modified .md files
|
||||
rename_md_files
|
||||
# Handle deletions
|
||||
handle_deletions
|
||||
# Generate index.html
|
||||
generate_index
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Signal Handling Functions
|
||||
# ============================
|
||||
|
||||
# Function to handle script termination gracefully
|
||||
cleanup() {
|
||||
log_warning "Script interrupted. Cleaning up..."
|
||||
# Terminate all background jobs
|
||||
jobs -rp | xargs -r kill -TERM 2>/dev/null || true
|
||||
exit "$EXIT_FAILURE"
|
||||
}
|
||||
|
||||
# Trap SIGINT and SIGTERM signals
|
||||
trap cleanup SIGINT SIGTERM
|
||||
|
||||
# ============================
|
||||
# Main Script Execution
|
||||
# ============================
|
||||
|
||||
main() {
|
||||
# Ensure that Bash is available
|
||||
check_bash
|
||||
|
||||
check_dependencies
|
||||
validate_paths
|
||||
|
||||
# Create TEMP_DIR if it doesn't exist
|
||||
if [[ ! -d "$TEMP_DIR" ]]; then
|
||||
log_info "Temporary directory '$TEMP_DIR' does not exist. Creating and performing full synchronization."
|
||||
mkdir -p "$TEMP_DIR"
|
||||
synchronize_markdown
|
||||
rename_md_files
|
||||
handle_deletions
|
||||
generate_index
|
||||
else
|
||||
log_info "Temporary directory '$TEMP_DIR' already exists. Checking for updates."
|
||||
update_if_needed
|
||||
fi
|
||||
|
||||
open_browser
|
||||
|
||||
log_success "All tasks completed successfully."
|
||||
exit "$EXIT_SUCCESS"
|
||||
}
|
||||
|
||||
# Invoke the main function
|
||||
main
|
||||
|
||||
575
nvim/scripts/vimwiki-markdown-preview.sh
Executable file
575
nvim/scripts/vimwiki-markdown-preview.sh
Executable file
@@ -0,0 +1,575 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# ============================
|
||||
# Exit Status Codes
|
||||
# ============================
|
||||
EXIT_SUCCESS=0
|
||||
EXIT_FAILURE=1
|
||||
EXIT_DEPENDENCY=2
|
||||
EXIT_CONVERSION=3
|
||||
|
||||
# ============================
|
||||
# Shell Options
|
||||
# ============================
|
||||
# Exit immediately if a command exits with a non-zero status,
|
||||
# Treat unset variables as an error,
|
||||
# Prevent errors in a pipeline from being masked
|
||||
set -euo pipefail
|
||||
|
||||
# ============================
|
||||
# Color Definitions
|
||||
# ============================
|
||||
RESET='\033[0m' # No Color
|
||||
BOLD='\033[1m'
|
||||
|
||||
# Regular Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
|
||||
# ============================
|
||||
# Configuration Constants
|
||||
# ============================
|
||||
|
||||
# Absolute paths ensure the script can be run from any directory
|
||||
SOURCE_DIR="$HOME/vimwiki" # Source Vimwiki directory
|
||||
TEMP_DIR="/tmp/vimwikihtml" # Temporary HTML output directory
|
||||
mkdir -p $TEMP_DIR
|
||||
CSS_FILE="$HOME/.local/share/nvim/style.css" # Path to the CSS file for styling
|
||||
CONCURRENT_JOBS=4 # Number of concurrent pandoc processes
|
||||
LOG_FILE="$TEMP_DIR/conversion.log" # Log file to track conversions
|
||||
touch $LOG_FILE
|
||||
ERROR_LOG_FILE="$TEMP_DIR/error.log" # Log file to track errors
|
||||
VERSION="1.0.0" # Script version
|
||||
|
||||
# ============================
|
||||
# Dependencies
|
||||
# ============================
|
||||
|
||||
DEPENDENCIES=("pandoc" "sed" "qutebrowser" "find" "rsync" "grep" "mkdir" "basename" "diff")
|
||||
|
||||
# ============================
|
||||
# Logging Functions
|
||||
# ============================
|
||||
|
||||
# Function to log INFO messages
|
||||
log_info() {
|
||||
local message="$1"
|
||||
echo -e "${BLUE}[INFO]${RESET} $message"
|
||||
echo "[INFO] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to log SUCCESS messages
|
||||
log_success() {
|
||||
local message="$1"
|
||||
echo -e "${GREEN}[SUCCESS]${RESET} $message"
|
||||
echo "[SUCCESS] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to log WARNING messages
|
||||
log_warning() {
|
||||
local message="$1"
|
||||
echo -e "${YELLOW}[WARNING]${RESET} $message"
|
||||
echo "[WARNING] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to log ERROR messages
|
||||
log_error() {
|
||||
local message="$1"
|
||||
echo -e "${RED}[ERROR]${RESET} $message" | tee -a "$ERROR_LOG_FILE"
|
||||
echo "[ERROR] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$ERROR_LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to handle errors with context and exit with specific code
|
||||
handle_error() {
|
||||
local message="$1"
|
||||
local exit_code="${2:-$EXIT_FAILURE}"
|
||||
local func="${FUNCNAME[1]}"
|
||||
local line="${BASH_LINENO[0]}"
|
||||
log_error "In function '$func' at line $line: $message"
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Filename Validation Function
|
||||
# ============================
|
||||
|
||||
# Function to check if a filename contains only allowed characters
|
||||
is_valid_filename() {
|
||||
local filename="$1"
|
||||
if [[ "$filename" =~ ^[A-Za-z0-9._-]+$ ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Bash Availability Check
|
||||
# ============================
|
||||
|
||||
# Function to ensure that Bash is available
|
||||
check_bash() {
|
||||
if ! command -v bash &>/dev/null; then
|
||||
echo -e "${RED}[ERROR]${RESET} Bash is not installed. Please install Bash to run this script."
|
||||
exit "$EXIT_FAILURE"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Path Validation Function
|
||||
# ============================
|
||||
|
||||
# Function to validate and sanitize input paths
|
||||
validate_paths() {
|
||||
log_info "Validating input paths..."
|
||||
|
||||
# Check that SOURCE_DIR is an absolute path
|
||||
if [[ "$SOURCE_DIR" != /* ]]; then
|
||||
handle_error "SOURCE_DIR ('$SOURCE_DIR') is not an absolute path." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
# Check that TEMP_DIR is an absolute path and under /tmp
|
||||
if [[ "$TEMP_DIR" != /tmp/* ]]; then
|
||||
handle_error "TEMP_DIR ('$TEMP_DIR') must be under /tmp." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
# Check that SOURCE_DIR exists and is a directory
|
||||
if [[ ! -d "$SOURCE_DIR" ]]; then
|
||||
handle_error "SOURCE_DIR ('$SOURCE_DIR') does not exist or is not a directory." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
# Check if TEMP_DIR exists; if not, it will be created later
|
||||
# If it exists, ensure it's a directory
|
||||
if [[ -e "$TEMP_DIR" && ! -d "$TEMP_DIR" ]]; then
|
||||
handle_error "TEMP_DIR ('$TEMP_DIR') exists but is not a directory." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
log_success "Input paths are valid."
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Function Definitions
|
||||
# ============================
|
||||
|
||||
# Function to check if all dependencies are installed
|
||||
check_dependencies() {
|
||||
log_info "Checking for required dependencies..."
|
||||
local missing_dependencies=()
|
||||
|
||||
for cmd in "${DEPENDENCIES[@]}"; do
|
||||
if ! command -v "$cmd" &>/dev/null; then
|
||||
missing_dependencies+=("$cmd")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#missing_dependencies[@]} -ne 0 ]]; then
|
||||
for cmd in "${missing_dependencies[@]}"; do
|
||||
handle_error "Dependency '$cmd' is not installed. Please install it and retry." "$EXIT_DEPENDENCY"
|
||||
done
|
||||
fi
|
||||
|
||||
log_success "All dependencies are satisfied."
|
||||
}
|
||||
|
||||
# Function to extract the title from Markdown using YAML frontmatter or fallback to filename
|
||||
extract_title() {
|
||||
local md_file="$1"
|
||||
# Attempt to extract title from YAML frontmatter
|
||||
local title
|
||||
title=$(grep -m1 '^title:' "$md_file" | sed 's/title: //') || true
|
||||
if [[ -z "$title" ]]; then
|
||||
# If no title found, use the filename without extension
|
||||
title=$(basename "$md_file" .md.old)
|
||||
fi
|
||||
echo "$title"
|
||||
}
|
||||
|
||||
# Function to convert a single Markdown file to HTML atomically
|
||||
convert_md_to_html() {
|
||||
local md_old_file="$1" # Path to the .md.old file
|
||||
# Determine the relative path from TEMP_DIR
|
||||
local relative_path="${md_old_file#$TEMP_DIR/}"
|
||||
# Remove .md.old extension
|
||||
relative_path="${relative_path%.md.old}"
|
||||
# Determine the output HTML file path
|
||||
local html_file="$TEMP_DIR/${relative_path}.html"
|
||||
# Determine the temporary HTML file path
|
||||
local temp_html_file="${html_file}.tmp"
|
||||
# Create the necessary directories for the HTML file
|
||||
mkdir -p "$(dirname "$html_file")"
|
||||
|
||||
# Extract the title for the HTML document
|
||||
local title
|
||||
title=$(extract_title "$md_old_file")
|
||||
|
||||
log_info "Converting '$md_old_file' to '$html_file'..."
|
||||
|
||||
# Use pandoc to convert Markdown to HTML with CSS and metadata
|
||||
if [[ -f "$CSS_FILE" ]]; then
|
||||
if ! pandoc -f markdown -s --css="$CSS_FILE" --metadata title="$title" "$md_old_file" -o "$temp_html_file"; then
|
||||
handle_error "Failed to convert '$md_old_file' to HTML." "$EXIT_CONVERSION"
|
||||
fi
|
||||
else
|
||||
log_warning "CSS file '$CSS_FILE' not found. Skipping CSS for '$md_old_file'."
|
||||
if ! pandoc -f markdown -s --metadata title="$title" "$md_old_file" -o "$temp_html_file"; then
|
||||
handle_error "Failed to convert '$md_old_file' to HTML." "$EXIT_CONVERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Debug: Print a snippet of the HTML file before running sed
|
||||
log_info "Snippet before sed in '$temp_html_file':"
|
||||
head -n 5 "$temp_html_file" || true
|
||||
echo "..."
|
||||
|
||||
# Adjust internal href links:
|
||||
# 1. Replace href="path/to/file.md.old" with href="path/to/file.html"
|
||||
# 2. Replace href="path/to/file" with href="path/to/file.html" only if 'file' has no extension
|
||||
log_info "Adjusting links in '$html_file'..."
|
||||
|
||||
# First, replace links ending with .md.old
|
||||
if ! sed -i -E 's|(href=")([^"#:/]+(/[^"#:/]+)*)\.md\.old(")|\1\2.html\4|g' "$temp_html_file"; then
|
||||
handle_error "Failed to adjust '.md.old' links in '$temp_html_file'." "$EXIT_CONVERSION"
|
||||
fi
|
||||
|
||||
# Then, replace links without any extension
|
||||
if ! sed -i -E 's|(href=")([^"#:/.]+(/[^"#:/.]+)*)(")|\1\2.html\4|g' "$temp_html_file"; then
|
||||
handle_error "Failed to adjust extensionless links in '$temp_html_file'." "$EXIT_CONVERSION"
|
||||
fi
|
||||
|
||||
# Adjust src attributes for images to prepend /tmp/vimwikihtml
|
||||
log_info "Adjusting image paths in '$temp_html_file'..."
|
||||
if ! sed -i -E 's|(<img[^>]*src=")(/[^"]*)(")|\1/tmp/vimwikihtml\2\3|g' "$temp_html_file"; then
|
||||
handle_error "Failed to adjust image paths in '$temp_html_file'." "$EXIT_CONVERSION"
|
||||
fi
|
||||
|
||||
# Move the temporary HTML file to the final destination atomically
|
||||
mv "$temp_html_file" "$html_file"
|
||||
|
||||
# Debug: Print a snippet of the HTML file after running sed
|
||||
log_info "Snippet after sed in '$html_file':"
|
||||
head -n 5 "$html_file" || true
|
||||
echo "..."
|
||||
|
||||
# Log the successful conversion
|
||||
echo "$(date +"%Y-%m-%d %H:%M:%S") - Converted '$md_old_file' to '$html_file'." >> "$LOG_FILE"
|
||||
|
||||
log_success "Successfully converted '$md_old_file' to '$html_file'."
|
||||
}
|
||||
|
||||
# Function to synchronize Markdown files and relevant assets to TEMP_DIR
|
||||
synchronize_markdown() {
|
||||
log_info "Synchronizing Markdown files and assets to '$TEMP_DIR'..."
|
||||
|
||||
# Use rsync to copy only .md, .pdf, and image files, excluding unwanted directories and files
|
||||
rsync -av --delete \
|
||||
--exclude='*.html' \
|
||||
--exclude='*.sh' \
|
||||
--exclude='.git/' \
|
||||
--exclude='.gitignore' \
|
||||
--exclude='*.bak' \
|
||||
--exclude='*.tex' \
|
||||
--exclude='*.toc' \
|
||||
--exclude='*.out' \
|
||||
--include='*/' \
|
||||
--include='*.md' \
|
||||
--include='*.pdf' \
|
||||
--include='*.png' \
|
||||
--include='*.jpg' \
|
||||
--include='*.jpeg' \
|
||||
--include='*.gif' \
|
||||
--exclude='*' \
|
||||
"$SOURCE_DIR/" "$TEMP_DIR/" | grep '\.md$' || true
|
||||
|
||||
log_success "Synchronization completed."
|
||||
}
|
||||
|
||||
# Function to rename .md files to .md.old in TEMP_DIR
|
||||
rename_md_files() {
|
||||
log_info "Renaming new or modified .md files to .md.old in '$TEMP_DIR'..."
|
||||
|
||||
# Find all .md files in TEMP_DIR
|
||||
find "$TEMP_DIR" -type f -name '*.md' | while IFS= read -r md_file; do
|
||||
# Determine the corresponding .md.old file
|
||||
md_old_file="${md_file}.old"
|
||||
|
||||
# Determine the source .md file
|
||||
source_md="$SOURCE_DIR/${md_file#$TEMP_DIR/}"
|
||||
|
||||
# Check if the .md.old file exists
|
||||
if [[ ! -f "$md_old_file" ]]; then
|
||||
# New file detected, copy to .md.old
|
||||
cp "$source_md" "$md_old_file"
|
||||
log_info "New file detected. Copied '$source_md' to '$md_old_file'."
|
||||
# Convert to HTML
|
||||
convert_md_to_html "$md_old_file" &
|
||||
else
|
||||
# Compare the source .md with the existing .md.old
|
||||
if ! diff -q "$source_md" "$md_old_file" &>/dev/null; then
|
||||
# Files differ, update .md.old and reconvert
|
||||
cp "$source_md" "$md_old_file"
|
||||
log_info "Modified file detected. Updated '$md_old_file' with changes from '$source_md'."
|
||||
# Convert to HTML
|
||||
convert_md_to_html "$md_old_file" &
|
||||
else
|
||||
log_info "No changes detected for '$source_md'. Skipping conversion."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Wait for all background conversions to finish
|
||||
wait
|
||||
|
||||
log_success "Renaming and conversion of new or modified .md files completed."
|
||||
}
|
||||
|
||||
# Function to handle deletions: Remove .html files corresponding to deleted .md files
|
||||
handle_deletions() {
|
||||
log_info "Handling deletions of Markdown files..."
|
||||
|
||||
# Find all .md.old files in TEMP_DIR
|
||||
find "$TEMP_DIR" -type f -name '*.md.old' | while IFS= read -r md_old_file; do
|
||||
# Determine the corresponding .md file in SOURCE_DIR
|
||||
source_md="$SOURCE_DIR/${md_old_file#$TEMP_DIR/}"
|
||||
source_md="${source_md%.md.old}.md"
|
||||
|
||||
# Check if the source .md file exists
|
||||
if [[ ! -f "$source_md" ]]; then
|
||||
# Corresponding .md file has been deleted, remove the .html file
|
||||
html_file="${md_old_file%.md.old}.html"
|
||||
if [[ -f "$html_file" ]]; then
|
||||
rm "$html_file"
|
||||
log_success "Deleted '$html_file' as the source Markdown file no longer exists."
|
||||
# Log the deletion
|
||||
echo "$(date +"%Y-%m-%d %H:%M:%S") - Deleted '$html_file' due to source removal." >> "$LOG_FILE"
|
||||
fi
|
||||
# Remove the .md.old file itself
|
||||
rm "$md_old_file"
|
||||
log_info "Removed obsolete '$md_old_file'."
|
||||
fi
|
||||
done
|
||||
|
||||
log_success "Deletion handling completed."
|
||||
}
|
||||
|
||||
# Function to generate index.html specifically
|
||||
generate_index() {
|
||||
local index_md_old="$TEMP_DIR/index.md.old"
|
||||
local index_html="$TEMP_DIR/index.html"
|
||||
|
||||
if [[ ! -f "$index_md_old" ]]; then
|
||||
handle_error "'index.md.old' not found in '$TEMP_DIR'." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
log_info "Generating 'index.html' from 'index.md.old'..."
|
||||
|
||||
# Convert the index.md.old file to HTML
|
||||
convert_md_to_html "$index_md_old"
|
||||
|
||||
# Ensure index.html exists
|
||||
if [[ ! -f "$index_html" ]]; then
|
||||
handle_error "Failed to generate 'index.html'." "$EXIT_CONVERSION"
|
||||
fi
|
||||
|
||||
log_success "'index.html' generation completed."
|
||||
}
|
||||
|
||||
# Function to open index.html in qutebrowser
|
||||
open_browser() {
|
||||
local index_file="$TEMP_DIR/index.html"
|
||||
if [[ -f "$index_file" ]]; then
|
||||
log_info "Opening '$index_file' in qutebrowser..."
|
||||
qutebrowser "$index_file" &
|
||||
log_success "Opened '$index_file' in qutebrowser."
|
||||
else
|
||||
handle_error "'$index_file' does not exist. Please ensure it is generated correctly." "$EXIT_FAILURE"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to display usage information
|
||||
usage() {
|
||||
echo -e "${BOLD}Usage:${RESET} $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --index-wiki, -iw Synchronize and convert Vimwiki to HTML, then open index.html in qutebrowser."
|
||||
echo " --help, -h Display this help message."
|
||||
echo " --version, -V Display the script's version."
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 --index-wiki"
|
||||
echo " $0 -iw"
|
||||
echo " $0 --help"
|
||||
echo " $0 -h"
|
||||
echo " $0 --version"
|
||||
echo " $0 -V"
|
||||
}
|
||||
|
||||
# Function to display version information
|
||||
version_info() {
|
||||
echo -e "${BOLD}Vimwiki HTML Converter${RESET} version ${GREEN}$VERSION${RESET}"
|
||||
}
|
||||
|
||||
# Function to update synchronization and conversion based on differences
|
||||
update_if_needed() {
|
||||
# Synchronize new and updated files
|
||||
synchronize_markdown
|
||||
# Rename and convert new or modified .md files
|
||||
rename_md_files
|
||||
# Handle deletions
|
||||
handle_deletions
|
||||
# Generate index.html
|
||||
generate_index
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Signal Handling Functions
|
||||
# ============================
|
||||
|
||||
# Function to handle script termination gracefully
|
||||
cleanup() {
|
||||
log_warning "Script interrupted. Cleaning up..."
|
||||
# Terminate all background jobs
|
||||
jobs -rp | xargs -r kill -TERM 2>/dev/null || true
|
||||
exit "$EXIT_FAILURE"
|
||||
}
|
||||
|
||||
# Trap SIGINT and SIGTERM signals
|
||||
trap cleanup SIGINT SIGTERM
|
||||
|
||||
# ============================
|
||||
# convert_single_file Function
|
||||
# ============================
|
||||
convert_single_file() {
|
||||
local md_file="$1"
|
||||
|
||||
# Validate filename
|
||||
local filename
|
||||
filename=$(basename "$md_file")
|
||||
if ! is_valid_filename "$filename"; then
|
||||
log_warning "Skipping file with invalid filename: '$md_file'"
|
||||
return
|
||||
fi
|
||||
|
||||
# Check if the file exists
|
||||
if [[ ! -f "$md_file" ]]; then
|
||||
handle_error "File '$md_file' does not exist." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
# Copy the Markdown file to the temporary directory
|
||||
local dest_dir="/tmp/markdowndump/"
|
||||
mkdir -p "$dest_dir"
|
||||
local dest_md_file="$dest_dir$filename"
|
||||
cp "$md_file" "$dest_md_file"
|
||||
log_info "Copied '$md_file' to '$dest_md_file'."
|
||||
|
||||
# Prepare the paths for HTML conversion
|
||||
local html_file="${dest_md_file%.md}.html"
|
||||
local temp_html_file="${html_file}.tmp"
|
||||
|
||||
# Extract the title for the HTML document
|
||||
local title
|
||||
title=$(extract_title "$dest_md_file")
|
||||
|
||||
# Convert Markdown to HTML using pandoc with CSS and metadata
|
||||
if [[ -f "$CSS_FILE" ]]; then
|
||||
if ! pandoc -f markdown -s --css="$CSS_FILE" --metadata title="$title" "$dest_md_file" -o "$temp_html_file"; then
|
||||
handle_error "Failed to convert '$dest_md_file' to HTML." "$EXIT_CONVERSION"
|
||||
fi
|
||||
else
|
||||
log_warning "CSS file '$CSS_FILE' not found. Skipping CSS for '$dest_md_file'."
|
||||
if ! pandoc -f markdown -s --metadata title="$title" "$dest_md_file" -o "$temp_html_file"; then
|
||||
handle_error "Failed to convert '$dest_md_file' to HTML." "$EXIT_CONVERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Move the temporary HTML file to the final destination
|
||||
mv "$temp_html_file" "$html_file"
|
||||
log_info "Converted Markdown file '$dest_md_file' to HTML '$html_file'."
|
||||
|
||||
# Open the HTML file in qutebrowser
|
||||
qutebrowser "$html_file" &
|
||||
log_success "Opened '$html_file' in qutebrowser."
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Main Script Execution
|
||||
# ============================
|
||||
|
||||
main() {
|
||||
# Parse command-line arguments
|
||||
if [[ $# -eq 0 ]]; then
|
||||
log_error "No arguments provided. Use --help for usage information."
|
||||
exit "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--index-wiki|-iw)
|
||||
action="index_wiki"
|
||||
shift
|
||||
;;
|
||||
--convert|-c)
|
||||
if [[ -z "${2:-}" ]]; then
|
||||
echo -e "${RED}[ERROR]${RESET} --convert flag requires a filename argument."
|
||||
exit "$EXIT_FAILURE"
|
||||
fi
|
||||
convert_single_file "$2"
|
||||
exit "$EXIT_SUCCESS"
|
||||
;;
|
||||
--help)
|
||||
usage
|
||||
exit "$EXIT_SUCCESS"
|
||||
;;
|
||||
-h)
|
||||
usage
|
||||
exit "$EXIT_SUCCESS"
|
||||
;;
|
||||
--version)
|
||||
version_info
|
||||
exit "$EXIT_SUCCESS"
|
||||
;;
|
||||
-V)
|
||||
version_info
|
||||
exit "$EXIT_SUCCESS"
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1. Use --help for usage information."
|
||||
exit "$EXIT_FAILURE"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Execute based on the action
|
||||
case "$action" in
|
||||
index_wiki)
|
||||
check_bash
|
||||
check_dependencies
|
||||
validate_paths
|
||||
# Create TEMP_DIR if it doesn't exist
|
||||
if [[ ! -d "$TEMP_DIR" ]]; then
|
||||
log_info "Temporary directory '$TEMP_DIR' does not exist. Creating and performing full synchronization."
|
||||
mkdir -p "$TEMP_DIR"
|
||||
synchronize_markdown
|
||||
rename_md_files
|
||||
handle_deletions
|
||||
generate_index
|
||||
else
|
||||
log_info "Temporary directory '$TEMP_DIR' already exists. Checking for updates."
|
||||
update_if_needed
|
||||
fi
|
||||
open_browser
|
||||
log_success "All tasks completed successfully."
|
||||
exit "$EXIT_SUCCESS"
|
||||
;;
|
||||
*)
|
||||
# This should not happen due to earlier checks
|
||||
log_error "Invalid action." "$EXIT_FAILURE"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Invoke the main function
|
||||
main "$@"
|
||||
|
||||
501
nvim/scripts/vimwiki-markdown-preview.sh.bak
Executable file
501
nvim/scripts/vimwiki-markdown-preview.sh.bak
Executable file
@@ -0,0 +1,501 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# ============================
|
||||
# Exit Status Codes
|
||||
# ============================
|
||||
EXIT_SUCCESS=0
|
||||
EXIT_FAILURE=1
|
||||
EXIT_DEPENDENCY=2
|
||||
EXIT_CONVERSION=3
|
||||
|
||||
# ============================
|
||||
# Shell Options
|
||||
# ============================
|
||||
# Exit immediately if a command exits with a non-zero status,
|
||||
# Treat unset variables as an error,
|
||||
# Prevent errors in a pipeline from being masked
|
||||
set -euo pipefail
|
||||
|
||||
# ============================
|
||||
# Color Definitions
|
||||
# ============================
|
||||
RESET='\033[0m' # No Color
|
||||
BOLD='\033[1m'
|
||||
|
||||
# Regular Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
BLUE='\033[0;34m'
|
||||
CYAN='\033[0;36m'
|
||||
|
||||
# ============================
|
||||
# Configuration Constants
|
||||
# ============================
|
||||
|
||||
# Absolute paths ensure the script can be run from any directory
|
||||
SOURCE_DIR="$HOME/vimwiki" # Source Vimwiki directory
|
||||
TEMP_DIR="/tmp/vimwikihtml" # Temporary HTML output directory
|
||||
CSS_FILE="$HOME/.local/share/nvim/style.css" # Path to the CSS file for styling
|
||||
CONCURRENT_JOBS=4 # Number of concurrent pandoc processes
|
||||
LOG_FILE="$TEMP_DIR/conversion.log" # Log file to track conversions
|
||||
ERROR_LOG_FILE="$TEMP_DIR/error.log" # Log file to track errors
|
||||
VERSION="1.0.0" # Script version
|
||||
|
||||
# ============================
|
||||
# Dependencies
|
||||
# ============================
|
||||
|
||||
DEPENDENCIES=("pandoc" "sed" "qutebrowser" "find" "rsync" "grep" "mkdir" "basename" "diff")
|
||||
|
||||
# ============================
|
||||
# Logging Functions
|
||||
# ============================
|
||||
|
||||
# Function to log INFO messages
|
||||
log_info() {
|
||||
local message="$1"
|
||||
echo -e "${BLUE}[INFO]${RESET} $message"
|
||||
echo "[INFO] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to log SUCCESS messages
|
||||
log_success() {
|
||||
local message="$1"
|
||||
echo -e "${GREEN}[SUCCESS]${RESET} $message"
|
||||
echo "[SUCCESS] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to log WARNING messages
|
||||
log_warning() {
|
||||
local message="$1"
|
||||
echo -e "${YELLOW}[WARNING]${RESET} $message"
|
||||
echo "[WARNING] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to log ERROR messages
|
||||
log_error() {
|
||||
local message="$1"
|
||||
echo -e "${RED}[ERROR]${RESET} $message" | tee -a "$ERROR_LOG_FILE"
|
||||
echo "[ERROR] $(date +"%Y-%m-%d %H:%M:%S") - $message" >> "$ERROR_LOG_FILE"
|
||||
}
|
||||
|
||||
# Function to handle errors with context and exit with specific code
|
||||
handle_error() {
|
||||
local message="$1"
|
||||
local exit_code="${2:-$EXIT_FAILURE}"
|
||||
local func="${FUNCNAME[1]}"
|
||||
local line="${BASH_LINENO[0]}"
|
||||
log_error "In function '$func' at line $line: $message"
|
||||
exit "$exit_code"
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Filename Validation Function
|
||||
# ============================
|
||||
|
||||
# Function to check if a filename contains only allowed characters
|
||||
is_valid_filename() {
|
||||
local filename="$1"
|
||||
if [[ "$filename" =~ ^[A-Za-z0-9._-]+$ ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Bash Availability Check
|
||||
# ============================
|
||||
|
||||
# Function to ensure that Bash is available
|
||||
check_bash() {
|
||||
if ! command -v bash &>/dev/null; then
|
||||
echo -e "${RED}[ERROR]${RESET} Bash is not installed. Please install Bash to run this script."
|
||||
exit "$EXIT_FAILURE"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Path Validation Function
|
||||
# ============================
|
||||
|
||||
# Function to validate and sanitize input paths
|
||||
validate_paths() {
|
||||
log_info "Validating input paths..."
|
||||
|
||||
# Check that SOURCE_DIR is an absolute path
|
||||
if [[ "$SOURCE_DIR" != /* ]]; then
|
||||
handle_error "SOURCE_DIR ('$SOURCE_DIR') is not an absolute path." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
# Check that TEMP_DIR is an absolute path and under /tmp
|
||||
if [[ "$TEMP_DIR" != /tmp/* ]]; then
|
||||
handle_error "TEMP_DIR ('$TEMP_DIR') must be under /tmp." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
# Check that SOURCE_DIR exists and is a directory
|
||||
if [[ ! -d "$SOURCE_DIR" ]]; then
|
||||
handle_error "SOURCE_DIR ('$SOURCE_DIR') does not exist or is not a directory." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
# Check if TEMP_DIR exists; if not, it will be created later
|
||||
# If it exists, ensure it's a directory
|
||||
if [[ -e "$TEMP_DIR" && ! -d "$TEMP_DIR" ]]; then
|
||||
handle_error "TEMP_DIR ('$TEMP_DIR') exists but is not a directory." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
log_success "Input paths are valid."
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Function Definitions
|
||||
# ============================
|
||||
|
||||
# Function to check if all dependencies are installed
|
||||
check_dependencies() {
|
||||
log_info "Checking for required dependencies..."
|
||||
local missing_dependencies=()
|
||||
|
||||
for cmd in "${DEPENDENCIES[@]}"; do
|
||||
if ! command -v "$cmd" &>/dev/null; then
|
||||
missing_dependencies+=("$cmd")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#missing_dependencies[@]} -ne 0 ]]; then
|
||||
for cmd in "${missing_dependencies[@]}"; do
|
||||
handle_error "Dependency '$cmd' is not installed. Please install it and retry." "$EXIT_DEPENDENCY"
|
||||
done
|
||||
fi
|
||||
|
||||
log_success "All dependencies are satisfied."
|
||||
}
|
||||
|
||||
# Function to extract the title from Markdown using YAML frontmatter or fallback to filename
|
||||
extract_title() {
|
||||
local md_file="$1"
|
||||
# Attempt to extract title from YAML frontmatter
|
||||
local title
|
||||
title=$(grep -m1 '^title:' "$md_file" | sed 's/title: //') || true
|
||||
if [[ -z "$title" ]]; then
|
||||
# If no title found, use the filename without extension
|
||||
title=$(basename "$md_file" .md.old)
|
||||
fi
|
||||
echo "$title"
|
||||
}
|
||||
|
||||
# Function to convert a single Markdown file to HTML atomically
|
||||
convert_md_to_html() {
|
||||
local md_old_file="$1" # Path to the .md.old file
|
||||
# Determine the relative path from TEMP_DIR
|
||||
local relative_path="${md_old_file#$TEMP_DIR/}"
|
||||
# Remove .md.old extension
|
||||
relative_path="${relative_path%.md.old}"
|
||||
# Determine the output HTML file path
|
||||
local html_file="$TEMP_DIR/${relative_path}.html"
|
||||
# Determine the temporary HTML file path
|
||||
local temp_html_file="${html_file}.tmp"
|
||||
# Create the necessary directories for the HTML file
|
||||
mkdir -p "$(dirname "$html_file")"
|
||||
|
||||
# Extract the title for the HTML document
|
||||
local title
|
||||
title=$(extract_title "$md_old_file")
|
||||
|
||||
log_info "Converting '$md_old_file' to '$html_file'..."
|
||||
|
||||
# Use pandoc to convert Markdown to HTML with CSS and metadata
|
||||
if [[ -f "$CSS_FILE" ]]; then
|
||||
if ! pandoc -f markdown -s --css="$CSS_FILE" --metadata title="$title" "$md_old_file" -o "$temp_html_file"; then
|
||||
handle_error "Failed to convert '$md_old_file' to HTML." "$EXIT_CONVERSION"
|
||||
fi
|
||||
else
|
||||
log_warning "CSS file '$CSS_FILE' not found. Skipping CSS for '$md_old_file'."
|
||||
if ! pandoc -f markdown -s --metadata title="$title" "$md_old_file" -o "$temp_html_file"; then
|
||||
handle_error "Failed to convert '$md_old_file' to HTML." "$EXIT_CONVERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Debug: Print a snippet of the HTML file before running sed
|
||||
log_info "Snippet before sed in '$temp_html_file':"
|
||||
head -n 5 "$temp_html_file" || true
|
||||
echo "..."
|
||||
|
||||
# Adjust internal href links:
|
||||
# 1. Replace href="path/to/file.md.old" with href="path/to/file.html"
|
||||
# 2. Replace href="path/to/file" with href="path/to/file.html" only if 'file' has no extension
|
||||
log_info "Adjusting links in '$html_file'..."
|
||||
|
||||
# First, replace links ending with .md.old
|
||||
if ! sed -i -E 's|(href=")([^"#:/]+(/[^"#:/]+)*)\.md\.old(")|\1\2.html\4|g' "$temp_html_file"; then
|
||||
handle_error "Failed to adjust '.md.old' links in '$temp_html_file'." "$EXIT_CONVERSION"
|
||||
fi
|
||||
|
||||
# Then, replace links without any extension
|
||||
if ! sed -i -E 's|(href=")([^"#:/.]+(/[^"#:/.]+)*)(")|\1\2.html\4|g' "$temp_html_file"; then
|
||||
handle_error "Failed to adjust extensionless links in '$temp_html_file'." "$EXIT_CONVERSION"
|
||||
fi
|
||||
|
||||
# Move the temporary HTML file to the final destination atomically
|
||||
mv "$temp_html_file" "$html_file"
|
||||
|
||||
# Debug: Print a snippet of the HTML file after running sed
|
||||
log_info "Snippet after sed in '$html_file':"
|
||||
head -n 5 "$html_file" || true
|
||||
echo "..."
|
||||
|
||||
# Log the successful conversion
|
||||
echo "$(date +"%Y-%m-%d %H:%M:%S") - Converted '$md_old_file' to '$html_file'." >> "$LOG_FILE"
|
||||
|
||||
log_success "Successfully converted '$md_old_file' to '$html_file'."
|
||||
}
|
||||
|
||||
# Function to synchronize Markdown files and relevant assets to TEMP_DIR
|
||||
synchronize_markdown() {
|
||||
log_info "Synchronizing Markdown files and assets to '$TEMP_DIR'..."
|
||||
|
||||
# Use rsync to copy only .md, .pdf, and image files, excluding unwanted directories and files
|
||||
rsync -av --delete \
|
||||
--exclude='*.html' \
|
||||
--exclude='*.sh' \
|
||||
--exclude='.git/' \
|
||||
--exclude='.gitignore' \
|
||||
--exclude='*.bak' \
|
||||
--include='*/' \
|
||||
--include='*.md' \
|
||||
--include='*.pdf' \
|
||||
--include='*.png' \
|
||||
--include='*.jpg' \
|
||||
--include='*.jpeg' \
|
||||
--include='*.gif' \
|
||||
--exclude='*' \
|
||||
"$SOURCE_DIR/" "$TEMP_DIR/" | grep '\.md$' || true
|
||||
|
||||
log_success "Synchronization completed."
|
||||
}
|
||||
|
||||
# Function to rename .md files to .md.old in TEMP_DIR
|
||||
rename_md_files() {
|
||||
log_info "Renaming new or modified .md files to .md.old in '$TEMP_DIR'..."
|
||||
|
||||
# Find all .md files in TEMP_DIR
|
||||
find "$TEMP_DIR" -type f -name '*.md' | while IFS= read -r md_file; do
|
||||
# Determine the corresponding .md.old file
|
||||
md_old_file="${md_file}.old"
|
||||
|
||||
# Determine the source .md file
|
||||
source_md="$SOURCE_DIR/${md_file#$TEMP_DIR/}"
|
||||
|
||||
# Check if the .md.old file exists
|
||||
if [[ ! -f "$md_old_file" ]]; then
|
||||
# New file detected, copy to .md.old
|
||||
cp "$source_md" "$md_old_file"
|
||||
log_info "New file detected. Copied '$source_md' to '$md_old_file'."
|
||||
# Convert to HTML
|
||||
convert_md_to_html "$md_old_file" &
|
||||
else
|
||||
# Compare the source .md with the existing .md.old
|
||||
if ! diff -q "$source_md" "$md_old_file" &>/dev/null; then
|
||||
# Files differ, update .md.old and reconvert
|
||||
cp "$source_md" "$md_old_file"
|
||||
log_info "Modified file detected. Updated '$md_old_file' with changes from '$source_md'."
|
||||
# Convert to HTML
|
||||
convert_md_to_html "$md_old_file" &
|
||||
else
|
||||
log_info "No changes detected for '$source_md'. Skipping conversion."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Wait for all background conversions to finish
|
||||
wait
|
||||
|
||||
log_success "Renaming and conversion of new or modified .md files completed."
|
||||
}
|
||||
|
||||
# Function to handle deletions: Remove .html files corresponding to deleted .md files
|
||||
handle_deletions() {
|
||||
log_info "Handling deletions of Markdown files..."
|
||||
|
||||
# Find all .md.old files in TEMP_DIR
|
||||
find "$TEMP_DIR" -type f -name '*.md.old' | while IFS= read -r md_old_file; do
|
||||
# Determine the corresponding .md file in SOURCE_DIR
|
||||
source_md="$SOURCE_DIR/${md_old_file#$TEMP_DIR/}"
|
||||
source_md="${source_md%.md.old}.md"
|
||||
|
||||
# Check if the source .md file exists
|
||||
if [[ ! -f "$source_md" ]]; then
|
||||
# Corresponding .md file has been deleted, remove the .html file
|
||||
html_file="${md_old_file%.md.old}.html"
|
||||
if [[ -f "$html_file" ]]; then
|
||||
rm "$html_file"
|
||||
log_success "Deleted '$html_file' as the source Markdown file no longer exists."
|
||||
# Log the deletion
|
||||
echo "$(date +"%Y-%m-%d %H:%M:%S") - Deleted '$html_file' due to source removal." >> "$LOG_FILE"
|
||||
fi
|
||||
# Remove the .md.old file itself
|
||||
rm "$md_old_file"
|
||||
log_info "Removed obsolete '$md_old_file'."
|
||||
fi
|
||||
done
|
||||
|
||||
log_success "Deletion handling completed."
|
||||
}
|
||||
|
||||
# Function to generate index.html specifically
|
||||
generate_index() {
|
||||
local index_md_old="$TEMP_DIR/index.md.old"
|
||||
local index_html="$TEMP_DIR/index.html"
|
||||
|
||||
if [[ ! -f "$index_md_old" ]]; then
|
||||
handle_error "'index.md.old' not found in '$TEMP_DIR'." "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
log_info "Generating 'index.html' from 'index.md.old'..."
|
||||
|
||||
# Convert the index.md.old file to HTML
|
||||
convert_md_to_html "$index_md_old"
|
||||
|
||||
# Ensure index.html exists
|
||||
if [[ ! -f "$index_html" ]]; then
|
||||
handle_error "Failed to generate 'index.html'." "$EXIT_CONVERSION"
|
||||
fi
|
||||
|
||||
log_success "'index.html' generation completed."
|
||||
}
|
||||
|
||||
# Function to open index.html in qutebrowser
|
||||
open_browser() {
|
||||
local index_file="$TEMP_DIR/index.html"
|
||||
if [[ -f "$index_file" ]]; then
|
||||
log_info "Opening '$index_file' in qutebrowser..."
|
||||
qutebrowser "$index_file" &
|
||||
log_success "Opened '$index_file' in qutebrowser."
|
||||
else
|
||||
handle_error "'$index_file' does not exist. Please ensure it is generated correctly." "$EXIT_FAILURE"
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to display usage information
|
||||
usage() {
|
||||
echo -e "${BOLD}Usage:${RESET} $0 [OPTIONS]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --index-wiki, -iw Synchronize and convert Vimwiki to HTML, then open index.html in qutebrowser."
|
||||
echo " --help, -h Display this help message."
|
||||
echo " --version, -V Display the script's version."
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 --index-wiki"
|
||||
echo " $0 -iw"
|
||||
echo " $0 --help"
|
||||
echo " $0 -h"
|
||||
echo " $0 --version"
|
||||
echo " $0 -V"
|
||||
}
|
||||
|
||||
# Function to display version information
|
||||
version_info() {
|
||||
echo -e "${BOLD}Vimwiki HTML Converter${RESET} version ${GREEN}$VERSION${RESET}"
|
||||
}
|
||||
|
||||
# Function to update synchronization and conversion based on differences
|
||||
update_if_needed() {
|
||||
# Synchronize new and updated files
|
||||
synchronize_markdown
|
||||
# Rename and convert new or modified .md files
|
||||
rename_md_files
|
||||
# Handle deletions
|
||||
handle_deletions
|
||||
# Generate index.html
|
||||
generate_index
|
||||
}
|
||||
|
||||
# ============================
|
||||
# Signal Handling Functions
|
||||
# ============================
|
||||
|
||||
# Function to handle script termination gracefully
|
||||
cleanup() {
|
||||
log_warning "Script interrupted. Cleaning up..."
|
||||
# Terminate all background jobs
|
||||
jobs -rp | xargs -r kill -TERM 2>/dev/null || true
|
||||
exit "$EXIT_FAILURE"
|
||||
}
|
||||
|
||||
# Trap SIGINT and SIGTERM signals
|
||||
trap cleanup SIGINT SIGTERM
|
||||
|
||||
# ============================
|
||||
# Main Script Execution
|
||||
# ============================
|
||||
|
||||
main() {
|
||||
# Parse command-line arguments
|
||||
if [[ $# -eq 0 ]]; then
|
||||
log_error "No arguments provided. Use --help for usage information."
|
||||
exit "$EXIT_FAILURE"
|
||||
fi
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--index-wiki|-iw)
|
||||
action="index_wiki"
|
||||
shift
|
||||
;;
|
||||
--help)
|
||||
usage
|
||||
exit "$EXIT_SUCCESS"
|
||||
;;
|
||||
-h)
|
||||
usage
|
||||
exit "$EXIT_SUCCESS"
|
||||
;;
|
||||
--version)
|
||||
version_info
|
||||
exit "$EXIT_SUCCESS"
|
||||
;;
|
||||
-V)
|
||||
version_info
|
||||
exit "$EXIT_SUCCESS"
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown option: $1. Use --help for usage information."
|
||||
exit "$EXIT_FAILURE"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Execute based on the action
|
||||
case "$action" in
|
||||
index_wiki)
|
||||
check_bash
|
||||
check_dependencies
|
||||
validate_paths
|
||||
# Create TEMP_DIR if it doesn't exist
|
||||
if [[ ! -d "$TEMP_DIR" ]]; then
|
||||
log_info "Temporary directory '$TEMP_DIR' does not exist. Creating and performing full synchronization."
|
||||
mkdir -p "$TEMP_DIR"
|
||||
synchronize_markdown
|
||||
rename_md_files
|
||||
handle_deletions
|
||||
generate_index
|
||||
else
|
||||
log_info "Temporary directory '$TEMP_DIR' already exists. Checking for updates."
|
||||
update_if_needed
|
||||
fi
|
||||
open_browser
|
||||
log_success "All tasks completed successfully."
|
||||
exit "$EXIT_SUCCESS"
|
||||
;;
|
||||
*)
|
||||
# This should not happen due to earlier checks
|
||||
log_error "Invalid action." "$EXIT_FAILURE"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Invoke the main function
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user