<commit_before>;;;; SPDX-FileCopyrightText: Atlas Engineer LLC
;;;; SPDX-License-Identifier: BSD-3-Clause

(in-package :nyxt)

(defun open-with-external-editor (&optional input-text)
  "Edit `input-text' using `external-editor-program'.
Create a temporary file and return its content.  The editor runs synchronously
so invoke on a separate thread when possible."
  (uiop:with-temporary-file (:directory (uiop:xdg-data-home +data-root+)
                             :pathname p)
    (when (> (length input-text) 0)
      (with-open-file (f p :direction :io
                           :if-exists :append)
        (write-sequence input-text f)))
    (log:debug "External Editor: ~s opening: ~s"
               (external-editor-program *browser*) p)
    (uiop:run-program (list (external-editor-program *browser*)
                            (uiop:native-namestring p))
                      :ignore-error-status t)
    (uiop:read-file-string p)))

(define-parenscript select-input-field ()
  (let ((active-tag (ps:chain document active-element tag-name)))
    (when (or (string= active-tag "INPUT")
              (string= active-tag "TEXTAREA"))
      (ps:chain document active-element (select)))))

(define-parenscript set-caret-on-end ()
  (ps:chain document (get-selection) (collapse-to-end)))

;; TODO:

;; BUG: Fails when the input field loses its focus, e.g the duckduckgo search
;; bar.  Can probably be solved with JS.

;; There could be an optional exiting behaviour -- set-caret-on-end or
;; undo-selection.

;; (define-parenscript undo-selection ()
;;   (ps:chain window (get-selection) (remove-all-ranges)))

;; It could be extended so that the coordinates of the cursor (line,column)
;; could be shared between Nyxt and the external editor.  A general solution
;; can't be achieved since not all editors, e.g. vi, accept the syntax
;; `+line:column' as an option to start the editor.

(define-command fill-input-from-external-editor ()
  "Edit the current input field using `external-editor-program'."
  (bt:make-thread
   (lambda ()
     (select-input-field)
     (%paste :input-text (open-with-external-editor (%copy)))
     (set-caret-on-end))))<commit_msg>external-editor: Catch errors when editor is unset or fails.<commit_after>;;;; SPDX-FileCopyrightText: Atlas Engineer LLC
;;;; SPDX-License-Identifier: BSD-3-Clause

(in-package :nyxt)

(defun edit-with-external-editor (&optional input-text)
  "Edit `input-text' using `external-editor-program'.
Create a temporary file and return its content.  The editor runs synchronously
so invoke on a separate thread when possible."
  (uiop:with-temporary-file (:directory (uiop:xdg-data-home +data-root+)
                             :pathname p)
    (when (> (length input-text) 0)
      (with-open-file (f p :direction :io
                           :if-exists :append)
        (write-sequence input-text f)))
    (log:debug "External editor ~s opens ~s"
               (external-editor-program *browser*) p)
    (with-muffled-body ("Failed editing: ~a" :condition)
      (uiop:run-program (list (external-editor-program *browser*)
                              (uiop:native-namestring p))
                        :ignore-error-status t))
    (uiop:read-file-string p)))

(define-parenscript select-input-field ()
  (let ((active-tag (ps:chain document active-element tag-name)))
    (when (or (string= active-tag "INPUT")
              (string= active-tag "TEXTAREA"))
      (ps:chain document active-element (select)))))

(define-parenscript set-caret-on-end ()
  (ps:chain document (get-selection) (collapse-to-end)))

;; TODO:

;; BUG: Fails when the input field loses its focus, e.g the duckduckgo search
;; bar.  Can probably be solved with JS.

;; There could be an optional exiting behaviour -- set-caret-on-end or
;; undo-selection.

;; (define-parenscript undo-selection ()
;;   (ps:chain window (get-selection) (remove-all-ranges)))

;; It could be extended so that the coordinates of the cursor (line,column)
;; could be shared between Nyxt and the external editor.  A general solution
;; can't be achieved since not all editors, e.g. vi, accept the syntax
;; `+line:column' as an option to start the editor.

(define-command fill-input-from-external-editor ()
  "Edit the current input field using `external-editor-program'."
  (if (external-editor-program *browser*)
      (bt:make-thread
       (lambda ()
         (select-input-field)
         (%paste :input-text (edit-with-external-editor (%copy)))
         (set-caret-on-end)))
      (echo-warning "Please set `external-editor-program' browser slot.")))
{'fraction_non_alphanumeric': 0.13394415357766143, 'fraction_numerical': 0.0008726003490401396, 'mean_word_length': 3.0683229813664594, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '331222', 'n_tokens_mistral': 1387, 'n_tokens_neox': 1362, 'n_words': 421}