diff --git a/src/interface/emacs/khoj.el b/src/interface/emacs/khoj.el index a36221b3..c4a60e57 100644 --- a/src/interface/emacs/khoj.el +++ b/src/interface/emacs/khoj.el @@ -49,8 +49,9 @@ (require 'url) (require 'json) (require 'transient) +(require 'outline) - + ;; ------------------------- ;; Khoj Static Configuration ;; ------------------------- @@ -84,7 +85,7 @@ (const "image") (const "music"))) - + ;; -------------------------- ;; Khoj Dynamic Configuration ;; -------------------------- @@ -160,7 +161,7 @@ Use `which-key` if available, else display simple message in echo area" nil t t)) (message "%s" (khoj--keybindings-info-message)))) - + ;; ----------------------------------------------- ;; Extract and Render Entries of each Content Type ;; ----------------------------------------------- @@ -247,7 +248,7 @@ Use `which-key` if available, else display simple message in echo area" ((and (member 'markdown enabled-content-types) (or (equal file-extension "markdown") (equal file-extension "md"))) "markdown") (t khoj-default-content-type)))) - + ;; -------------- ;; Query Khoj API ;; -------------- @@ -274,9 +275,8 @@ Use `which-key` if available, else display simple message in echo area" (encoded-query (url-hexify-string query))) (format "%s/api/search?q=%s&t=%s&r=%s&n=%s" khoj-server-url encoded-query content-type rerank khoj-results-count))) -(defun khoj--query-api-and-render-results (query content-type query-url buffer-name) - "Query Khoj API using QUERY, CONTENT-TYPE, QUERY-URL. -Render results in BUFFER-NAME." +(defun khoj--query-api-and-render-results (query-url content-type query buffer-name) + "Query Khoj QUERY-URL. Render results in BUFFER-NAME using QUERY, CONTENT-TYPE." ;; get json response from api (with-current-buffer buffer-name (let ((inhibit-read-only t) @@ -304,7 +304,7 @@ Render results in BUFFER-NAME." (t (fundamental-mode)))) (read-only-mode t))) - + ;; ------------------ ;; Incremental Search ;; ------------------ @@ -334,9 +334,9 @@ Render results in BUFFER-NAME." (setq khoj--rerank t) (message "Khoj: Rerank Results")) (khoj--query-api-and-render-results - query - khoj--content-type query-url + khoj--content-type + query khoj-buffer-name)))))) (defun khoj--delete-open-network-connections-to-server () @@ -378,7 +378,73 @@ Render results in BUFFER-NAME." (add-hook 'minibuffer-exit-hook #'khoj--teardown-incremental-search)) ; teardown khoj incremental search on minibuffer exit (read-string khoj--query-prompt)))) + +;; -------------- +;; Similar Search +;; -------------- +(defun khoj--get-current-outline-entry-text () + "Get text under current outline section." + (with-current-buffer (current-buffer) + ;; jump to cursor in current buffer + (goto-char (point)) + ;; trim leading whitespaces from text + (replace-regexp-in-string + "^[ \t\n]*" "" + ;; trim trailing whitespaces from text + (replace-regexp-in-string + "[ \t\n]*$" "" + ;; get text of current outline entry + (buffer-substring-no-properties + (save-excursion (outline-previous-heading) (point)) + (save-excursion (outline-next-heading) (point))))))) + +(defun khoj--get-current-paragraph-text () + "Get text in current paragraph at point." + (with-current-buffer (current-buffer) + ;; jump to cursor in current buffer + (goto-char (point)) + ;; trim leading whitespaces from text + (replace-regexp-in-string + "^[ \t\n]*" "" + ;; trim trailing whitespaces from text + (replace-regexp-in-string + "[ \t\n]*$" "" + ;; get text of current entry + (buffer-substring-no-properties + (save-excursion (backward-paragraph) (point)) + (save-excursion (forward-paragraph) (point))))))) + +(defun khoj--find-similar (&optional content-type) + "Find items of CONTENT-TYPE in khoj index similar to text surrounding point." + (interactive) + (let* ((rerank "true") + ;; set content type to: specified > based on current buffer > default type + (content-type (or content-type (khoj--buffer-name-to-content-type (buffer-name)))) + ;; get text surrounding current point based on the major mode context + (query (cond + ;; get section outline derived mode like org or markdown + ((or (derived-mode-p 'outline-mode) (equal major-mode 'markdown-mode)) + (khoj--get-current-outline-entry-text)) + ;; get paragraph, if in text mode + (t + (khoj--get-current-paragraph-text)))) + (query-url (khoj--construct-api-query query content-type rerank)) + ;; extract heading to show in result buffer from query + (query-title + (format "Similar to: %s" + (replace-regexp-in-string "^[#\\*]* " "" (car (split-string query "\n"))))) + (buffer-name (get-buffer-create khoj--buffer-name))) + (progn + (khoj--query-api-and-render-results + query-url + content-type + query-title + buffer-name) + (switch-to-buffer buffer-name) + (goto-char (point-min))))) + + ;; --------- ;; Khoj Menu ;; --------- @@ -387,7 +453,7 @@ Render results in BUFFER-NAME." :class 'transient-switches :argument-format "--content-type=%s" :argument-regexp ".+" - ;; set content type to last used or based on current buffer or to default + ;; set content type to: last used > based on current buffer > default type :init-value (lambda (obj) (oset obj value (format "--content-type=%s" (or khoj--content-type (khoj--buffer-name-to-content-type (buffer-name)))))) ;; dynamically set choices to content types enabled on khoj backend :choices (or (ignore-errors (mapcar #'symbol-name (khoj--get-enabled-content-types))) '("org" "markdown" "ledger" "music" "image"))) @@ -395,21 +461,34 @@ Render results in BUFFER-NAME." (transient-define-suffix khoj--search-command (&optional args) (interactive (list (transient-args transient-current-command))) (progn - ;; set content type to last used or based on current buffer or to default + ;; set content type to: specified > last used > based on current buffer > default type (setq khoj--content-type (or (transient-arg-value "--content-type=" args) (khoj--buffer-name-to-content-type (buffer-name)))) - ;; set results count to last used or to default + ;; set results count to: specified > last used > to default (setq khoj-results-count (or (transient-arg-value "--results-count=" args) khoj-results-count)) ;; trigger incremental search (call-interactively #'khoj-incremental))) +(transient-define-suffix khoj--find-similar-command (&optional args) + "Find items similar to current item at point." + (interactive (list (transient-args transient-current-command))) + (progn + ;; set content type to: specified > last used > based on current buffer > default type + (setq khoj--content-type (or (transient-arg-value "--content-type=" args) (khoj--buffer-name-to-content-type (buffer-name)))) + ;; set results count to: specified > last used > to default + (setq khoj-results-count (or (transient-arg-value "--results-count=" args) khoj-results-count)) + (khoj--find-similar khoj--content-type))) + (transient-define-suffix khoj--update-command (&optional args) "Call khoj API to update index of specified content type." (interactive (list (transient-args transient-current-command))) (let* ((force-update (if (member "--force-update" args) "true" "false")) + ;; set content type to: specified > last used > based on current buffer > default type (content-type (or (transient-arg-value "--content-type=" args) (khoj--buffer-name-to-content-type (buffer-name)))) (update-url (format "%s/api/update?t=%s&force=%s" khoj-server-url content-type force-update)) (url-request-method "GET")) - (url-retrieve update-url (lambda (_) (message "Khoj %s index %supdated!" content-type (if (member "--force-update" args) "force " "")))))) + (progn + (setq khoj--content-type content-type) + (url-retrieve update-url (lambda (_) (message "Khoj %s index %supdated!" content-type (if (member "--force-update" args) "force " ""))))))) (transient-define-prefix khoj-menu () "Create Khoj Menu to Configure and Execute Commands." @@ -421,10 +500,11 @@ Render results in BUFFER-NAME." ("-f" "Force Update" "--force-update")]] [["Act" ("s" "Search" khoj--search-command) + ("f" "Find Similar" khoj--find-similar-command) ("u" "Update" khoj--update-command) ("q" "Quit" transient-quit-one)]]) - + ;; ---------- ;; Entrypoint ;; ----------