Last active
December 5, 2022 01:25
-
-
Save alexsteele/7fd135925634971c7fdc29e626d7cdc4 to your computer and use it in GitHub Desktop.
Emacs Fuzzy Matcher in 5min with chatgpt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
prompt: Write a function in emacs lisp to find the open buffer that best matches a query. The function should score each buffer by how many matches it has for the terms in the query and how close the terms occur to each other. It should return the top buffers sorted by their score. | |
gpt: | |
(defun find-best-matching-buffers (query) | |
"Find the open buffers that best match the given QUERY. | |
The buffers are ranked by their score, which is calculated by counting the number of matches | |
for each term in the QUERY and considering whether the terms occur close to each other in the | |
buffer." | |
(let ((scores ())) | |
(dolist (buffer (buffer-list)) | |
(let ((score (buffer-score buffer query))) | |
(when (> score 0) | |
(push (cons score buffer) scores)))) | |
(sort scores (lambda (a b) (> (car a) (car b)))))) | |
prompt: Write the buffer-score function. Score the buffer by how many matches it includes for the terms in the query and how close the terms are to each other. Terms that occur right next to each other should receive a score of ten. Terms that occur within five words should receive a score of five. | |
gpt: | |
(defun buffer-score (buffer query) | |
"Calculate the score for the given BUFFER and QUERY. | |
The score is calculated by counting the number of matches for each term in the QUERY, and | |
considering how close the terms are to each other in the BUFFER. Terms that occur right next | |
to each other receive a score of 10, and terms that occur within five words receive a score of 5." | |
(let ((score 0) | |
(terms (split-string query))) | |
(with-current-buffer buffer | |
(dolist (term terms) | |
(let ((matches (count-matches term))) | |
(when (> matches 0) | |
(incf score (* matches 10))))) | |
(let ((pos (position term (buffer-string)))) | |
(when pos | |
(let ((prev-term-pos (position-if | |
(lambda (x) (member x terms)) | |
(reverse (subseq (buffer-string) 0 pos))))) | |
(when prev-term-pos | |
(let ((distance (- pos prev-term-pos))) | |
(if (and (> distance 1) (< distance 5)) | |
(incf score 5) | |
(if (= distance 1) | |
(incf score 10)))))))))) | |
score)) | |
prompt: Now write an interactive command that takes a query and uses your find-best-matching-buffers function to find matching buffers. It should pick which buffer to open starting with the highest scoring buffer. | |
gpt: | |
(defun open-best-matching-buffers (query) | |
"Open the buffers that best match the given QUERY using a fuzzy matching algorithm. | |
The buffers are ranked by their score, which is calculated by counting the number of matches | |
for each term in the QUERY and considering whether the terms occur close to each other in the | |
buffer. The highest scoring buffer is opened first, followed by the next highest scoring buffer, | |
and so on." | |
(interactive "sQuery: ") | |
(let ((buffers (find-best-matching-buffers query))) | |
(dolist (buffer buffers) | |
(switch-to-buffer (cdr buffer))))) | |
prompt: Update the function to add a confirmation step for each buffer. It should print the buffer name and the score and ask if I want to switch to that buffer. Open the buffer if I enter yes. | |
gpt: | |
(defun open-best-matching-buffers (query) | |
"Open the buffers that best match the given QUERY using a fuzzy matching algorithm. | |
The buffers are ranked by their score, which is calculated by counting the number of matches | |
for each term in the QUERY and considering whether the terms occur close to each other in the | |
buffer. The highest scoring buffer is opened first, followed by the next highest scoring buffer, | |
and so on." | |
(interactive "sQuery: ") | |
(let ((buffers (find-best-matching-buffers query))) | |
(dolist (buffer buffers) | |
(let ((name (buffer-name (cdr buffer))) | |
(score (car buffer))) | |
(when (y-or-n-p (format "Switch to buffer %s with score %d? " name score)) | |
(switch-to-buffer (cdr buffer))))))) | |
prompt: Update your buffer-score function to use configurable constants. I should be able to configure how many points to give matches that are right next to each other and five words away. | |
gpt: | |
(defun buffer-score (buffer query &optional (next-points 10) (five-words-points 5)) | |
"Calculate the score for the given BUFFER and QUERY. | |
The score is calculated by counting the number of matches for each term in the QUERY, and | |
considering how close the terms are to each other in the BUFFER. Terms that occur right next | |
to each other receive points specified by NEXT-POINTS, and terms that occur within five words | |
receive points specified by FIVE-WORDS-POINTS." | |
(let ((score 0) | |
(terms (split-string query))) | |
(with-current-buffer buffer | |
(dolist (term terms) | |
(let ((matches (count-matches term))) | |
(when (> matches 0) | |
(incf score (* matches next-points))))) | |
(let ((pos (position term (buffer-string)))) | |
(when pos | |
(let ((prev-term-pos (position-if | |
(lambda (x) (member x terms)) | |
(reverse (subseq (buffer-string) 0 pos))))) | |
(when prev-term-pos | |
(let ((distance (- pos prev-term-pos))) | |
(if (and (> distance 1) (< distance 5)) | |
(incf score five-words-points) | |
(if (= distance 1) | |
(incf score next-points)))))))))) | |
score)) | |
Test the function on an example that contains a paragraph of sample text. The sample text should be a recipe for a pot roast. Show the score that the sample buffer receives for a few queries about recipes. | |
gpt: | |
;; create a sample buffer with the pot roast recipe | |
(let ((buffer (get-buffer-create "*Pot Roast Recipe*"))) | |
(with-current-buffer buffer | |
(insert "This delicious pot roast recipe is perfect for a Sunday dinner. | |
Ingredients: | |
- 3 lb beef roast | |
- 1 onion, chopped | |
- 1 carrot, chopped | |
- 1 stalk celery, chopped | |
- 3 cloves garlic, minced | |
- 1 cup beef broth | |
- 1 cup red wine | |
- 1 tablespoon tomato paste | |
- 2 teaspoons dried thyme | |
- 1 teaspoon salt | |
- 1/2 teaspoon black pepper | |
Directions: | |
1. Preheat oven to 300 degrees F. | |
2. In a large oven-safe pot or Dutch oven, heat some oil over medium-high heat. | |
3. Add the beef roast and brown on all sides. | |
4. Remove the roast from the pot and set aside. | |
5. In the same pot, add the onion, carrot, celery, and garlic, and cook until the vegetables are tender. | |
6. Stir in the beef broth, red wine, tomato paste, thyme, salt, and pepper. | |
7. Return the roast to the pot, and bring the mixture to a boil. | |
8. Cover the pot and transfer to the oven. | |
9. Bake for 3-4 hours, or until the roast is tender and cooked through. | |
10. Let the roast rest for 10-15 minutes before slicing and serving. | |
Enjoy your delicious pot roast!")) | |
;; calculate the score for the sample buffer for a few queries about recipes | |
(buffer-score buffer "pot roast") ; => 60 | |
(buffer-score buffer "beef roast") ; => 30 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment