Hey all!
New to Nim but not to programming, I had an idea for a command line notetaker (original I know...) that would work in the "unix" way. A lot of others seem to use a DB to store your notes (yuck) or have opaque hard-to-navigate interfaces.
Out of my frustration I created "jn" which works out of the box and if you have one installed, uses fuzzy finding to create/edit/search notes.
Thought I'd share it here since y'know...Nim.
https://github.com/joereynolds/jn
P.S.
I am very very new to Nim. Please feel free to flame my code, I have no idea what I'm doing hah
Well the obvious question here is: why does it depend on ripgrep and fzf or fzy? Make it use nimgrep or just embed some search and fuzzy match logic into your program.
Welcome to Nim!
Thanks!
Removing the ripgrep dependency is actually on my todo list. The fzf/fzy dependency is a bit more baked in, purely because I have never written anything like that before and would likely make something worse than what they're offering.
I'll have to study that a bit more heh...
Thanks for championing Nim, I'm having a blast
It seems like everyone writes something like that in their career :D. Here is mine: https://github.com/enthus1ast/nimTodo
I've half vibe coded a fuzzy search (like fzf does it) in Nim, it's somewhere in my messy temp folder.... I might find it again
There it is:
import std/[strutils, sequtils, algorithm]
type
FuzzyResult = object
target: string
score: int
proc fuzzyMatch(pattern, target: string): (bool, int) =
if pattern.len == 0: return (true, 0)
var
score = 0
pIdx = 0
tIdx = 0
lastMatchIdx = -1
let
p = pattern.toLowerAscii()
t = target.toLowerAscii()
while pIdx < p.len and tIdx < t.len:
if p[pIdx] == t[tIdx]:
# --- Scoring Logic ---
# Bonus for consecutive matches
if lastMatchIdx != -1 and tIdx == lastMatchIdx + 1:
score += 10
# Bonus for matching the start of the string
if tIdx == 0:
score += 5
# Bonus for matching after a separator (path-aware)
if tIdx > 0 and t[tIdx-1] in {'/', '\\', '_', '-', '.'}:
score += 8
lastMatchIdx = tIdx
pIdx += 1
tIdx += 1
# If we exhausted the pattern, it's a match
return (pIdx == p.len, score)
# --- Test Drive ---
let items = @["src/main.nim", "tests/test1.nim", "README.md", "nimble.toml", "src/parser.nim"]
let query = "snim" # Searching for 's' and 'nim'
var results: seq[FuzzyResult]
for item in items:
let (matched, score) = fuzzyMatch(query, item)
if matched:
results.add(FuzzyResult(target: item, score: score))
# Sort by score (descending)
results.sort(proc (x, y: FuzzyResult): int = cmp(y.score, x.score))
for res in results:
echo "Score: ", res.score, " -> ", res.target Good presentation. I especially like the movie.
If i hadnt my own solution i would definitely try it!
It seems like everyone writes something like that in their career :D
True =) I already have two similar projects:
memento - todo and notes with fuzzy matching (uses a nimble package)
I wrote it in Nim in a single session over a year ago, never looked at the code again. It is probably terrible, but works for me.
tempo - creates and manages throwaway files with timestamp as a name. It's a shell script with linux utils, but I might rewrite it in Nim later.
I wanted a single command to create new file for writing short Nim experiments, where I don't have to think about name or place:
alias enim="tempo -xnim -e"
enim # open new file
enim 1 # open most recent file
nim r `enim -p` # compile and run
enim -d 1 # delete most recent file