Files
joakim 1d87d93172 Add multi-depository support with global config
- Implement global CLI config at ~/.config/jade/config.yml
- Add jade depo commands (add, list, remove, set-default)
- Support depository short names and context-aware detection
- Remove tag_prefix config, hardcode + syntax for consistency
- Update depository resolution: flag -> context -> default
- Auto-initialize .jade/ directory structure when adding depos
- Update documentation with new multi-depository workflow
2026-01-03 16:25:23 +01:00

144 lines
3.3 KiB
Go

package engine
import (
"bufio"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"time"
)
type Note struct {
Path string
Title string
Tags []string
Links []string
Created time.Time
Modified time.Time
}
// LoadNote reads a note from the filesystem and parses its content
func LoadNote(depoPath, notePath string) (*Note, error) {
fullPath := filepath.Join(depoPath, notePath)
// Get file info
info, err := os.Stat(fullPath)
if err != nil {
return nil, fmt.Errorf("failed to stat note: %w", err)
}
// Read file content
content, err := os.ReadFile(fullPath)
if err != nil {
return nil, fmt.Errorf("failed to read note: %w", err)
}
note := &Note{
Path: notePath,
Modified: info.ModTime(),
Created: info.ModTime(), // We'll use modified time as created for now
}
// Parse title (first # heading)
note.Title = parseTitle(string(content))
if note.Title == "" {
// Use filename without extension as fallback
note.Title = strings.TrimSuffix(filepath.Base(notePath), filepath.Ext(notePath))
}
// Parse tags (hardcoded to + prefix)
note.Tags = parseTags(string(content))
// Parse links
note.Links = parseLinks(string(content))
return note, nil
}
// parseTitle extracts the first # heading from markdown content
func parseTitle(content string) string {
scanner := bufio.NewScanner(strings.NewReader(content))
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if strings.HasPrefix(line, "# ") {
return strings.TrimSpace(strings.TrimPrefix(line, "#"))
}
}
return ""
}
// parseTags extracts tags from markdown content using + prefix
func parseTags(content string) []string {
// Match + followed by word characters
re := regexp.MustCompile(`\+\w+`)
matches := re.FindAllString(content, -1)
// Remove duplicates
tagSet := make(map[string]bool)
var tags []string
for _, tag := range matches {
if !tagSet[tag] {
tagSet[tag] = true
tags = append(tags, tag)
}
}
return tags
}
// parseLinks extracts both [[wiki-style]] and [markdown](links) from content
func parseLinks(content string) []string {
var links []string
linkSet := make(map[string]bool)
// Match [[wiki-style links]]
wikiRe := regexp.MustCompile(`\[\[([^\]]+)\]\]`)
wikiMatches := wikiRe.FindAllStringSubmatch(content, -1)
for _, match := range wikiMatches {
if len(match) > 1 {
link := match[1]
if !linkSet[link] {
linkSet[link] = true
links = append(links, link)
}
}
}
// Match [text](markdown-links)
mdRe := regexp.MustCompile(`\[([^\]]+)\]\(([^)]+)\)`)
mdMatches := mdRe.FindAllStringSubmatch(content, -1)
for _, match := range mdMatches {
if len(match) > 2 {
link := match[2]
// Only include .md files (note links, not external URLs)
if strings.HasSuffix(link, ".md") && !strings.HasPrefix(link, "http") {
if !linkSet[link] {
linkSet[link] = true
links = append(links, link)
}
}
}
}
return links
}
// TitleToFilename converts a note title to a valid filename
func TitleToFilename(title string) string {
// Convert to lowercase
filename := strings.ToLower(title)
// Replace spaces with hyphens
filename = strings.ReplaceAll(filename, " ", "-")
// Remove or replace special characters
re := regexp.MustCompile(`[^a-z0-9\-_]`)
filename = re.ReplaceAllString(filename, "")
// Add .md extension
return filename + ".md"
}