Add jshell stuff
This commit is contained in:
		
							
								
								
									
										287
									
								
								elisp/inferior-jshell.el
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								elisp/inferior-jshell.el
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,287 @@
 | 
			
		||||
;;; inferior-jshell.el --- Run JShell in a comint buffer  -*- lexical-binding: t; -*-
 | 
			
		||||
;;; Commentary:
 | 
			
		||||
;;; Code:
 | 
			
		||||
(require 'comint)
 | 
			
		||||
(require 'cl-lib)
 | 
			
		||||
(require 'cc-mode)
 | 
			
		||||
(require 'treesit)
 | 
			
		||||
(eval-when-compile (require 'rx))
 | 
			
		||||
 | 
			
		||||
(defgroup inferior-jshell ()
 | 
			
		||||
  "Run JShell in a comint buffer."
 | 
			
		||||
  :group 'comint)
 | 
			
		||||
 | 
			
		||||
(defvar jshell-buffer-name "*jshell*"
 | 
			
		||||
  "Name to use for inferior jshell process buffer.")
 | 
			
		||||
 | 
			
		||||
(defvar jshell-program "jshell"
 | 
			
		||||
  "The program to default to for `run-jshell'.")
 | 
			
		||||
 | 
			
		||||
(defvar jshell-switches ()
 | 
			
		||||
  "List of arguments to pass to jshell in `run-jshell'.")
 | 
			
		||||
 | 
			
		||||
(defvar-local jshell--fontification-buffer nil
 | 
			
		||||
  "The fontification buffer for the current jshell buffer.")
 | 
			
		||||
 | 
			
		||||
(defvar-local jshell--skip-next-lines 0
 | 
			
		||||
  "Number of lines of output to skip.")
 | 
			
		||||
 | 
			
		||||
(defun jshell--preoutput-filter-function (output)
 | 
			
		||||
  "Preoutput filter function for jshell.
 | 
			
		||||
OUTPUT is the new text to be inserted."
 | 
			
		||||
  (if (<= jshell--skip-next-lines 0)
 | 
			
		||||
      output
 | 
			
		||||
    (let* ((lines (string-lines output))
 | 
			
		||||
           (cnt (length lines)))
 | 
			
		||||
      (if (> cnt jshell--skip-next-lines)
 | 
			
		||||
          (prog1
 | 
			
		||||
              (string-join (nthcdr jshell--skip-next-lines lines) "\n")
 | 
			
		||||
            (setq jshell--skip-next-lines 0))
 | 
			
		||||
        (cl-decf jshell--skip-next-lines cnt)
 | 
			
		||||
        (when (and (not (string-empty-p output))
 | 
			
		||||
                   (/= ?\n (elt output (1- (length output)))))
 | 
			
		||||
          (cl-incf jshell--skip-next-lines))
 | 
			
		||||
        ""))))
 | 
			
		||||
 | 
			
		||||
(defun jshell--get-fontification-buffer ()
 | 
			
		||||
  "Return the jshell fontification buffer."
 | 
			
		||||
  (if (buffer-live-p jshell--fontification-buffer)
 | 
			
		||||
      jshell--fontification-buffer
 | 
			
		||||
    (let ((buffer (generate-new-buffer
 | 
			
		||||
                   (format " %s-fontification-buffer" (buffer-name)))))
 | 
			
		||||
      (with-current-buffer buffer
 | 
			
		||||
        (unless (derived-mode-p 'c++-mode)
 | 
			
		||||
          (let ((delayed-mode-hooks nil))
 | 
			
		||||
            (delay-mode-hooks
 | 
			
		||||
              (java-mode))))
 | 
			
		||||
        (when (eq c-basic-offset 'set-from-style)
 | 
			
		||||
          (setq-local c-basic-offset standard-indent))
 | 
			
		||||
        (let ((inhibit-message t))
 | 
			
		||||
          (indent-tabs-mode -1))
 | 
			
		||||
        (unless font-lock-mode
 | 
			
		||||
          (font-lock-mode 1)))
 | 
			
		||||
      (setq-local jshell--fontification-buffer buffer))))
 | 
			
		||||
 | 
			
		||||
(defmacro jshell--with-font-lock-buffer (&rest body)
 | 
			
		||||
  "Execute BODY in the jshell indirect buffer.
 | 
			
		||||
Note that this erases the buffer before doing anything."
 | 
			
		||||
  `(with-current-buffer (jshell--get-fontification-buffer)
 | 
			
		||||
     (erase-buffer)
 | 
			
		||||
     ,@body))
 | 
			
		||||
 | 
			
		||||
(defun jshell--fontify-current-input ()
 | 
			
		||||
  "Function called from `post-command-hook' to fontify the current input."
 | 
			
		||||
  (when-let ((proc (get-buffer-process (current-buffer)))
 | 
			
		||||
             (start (process-mark proc))
 | 
			
		||||
             (end (point-max))
 | 
			
		||||
             (input (buffer-substring-no-properties start end))
 | 
			
		||||
             (fontified (jshell--with-font-lock-buffer
 | 
			
		||||
                         (insert input)
 | 
			
		||||
                         (font-lock-ensure)
 | 
			
		||||
                         (buffer-string)))
 | 
			
		||||
             (len (length fontified))
 | 
			
		||||
             (i 0))
 | 
			
		||||
    ;; mostly from:
 | 
			
		||||
    ;; `python-shell-font-lock-post-command-hook'
 | 
			
		||||
    (while (not (= i len))
 | 
			
		||||
      (let* ((props (text-properties-at i fontified))
 | 
			
		||||
             (change-i (or (next-property-change i fontified)
 | 
			
		||||
                           len)))
 | 
			
		||||
        (when-let ((face (plist-get props 'face)))
 | 
			
		||||
          (setf (plist-get props 'face) nil
 | 
			
		||||
                (plist-get props 'font-lock-face) face))
 | 
			
		||||
        (set-text-properties (+ start i) (+ start change-i) props)
 | 
			
		||||
        (setq i change-i)))))
 | 
			
		||||
 | 
			
		||||
(defun jshell--bounds-of-last-prompt ()
 | 
			
		||||
  "Return the bounds of the last jshell prompt.
 | 
			
		||||
This returns a cons."
 | 
			
		||||
  (save-excursion
 | 
			
		||||
    (let ((end (process-mark (get-buffer-process (current-buffer)))))
 | 
			
		||||
      (goto-char end)
 | 
			
		||||
      (cons (pos-bol) end))))
 | 
			
		||||
 | 
			
		||||
(defun jshell--remove-extra-indentation (count)
 | 
			
		||||
  "Remove COUNT spaces from the start of each line."
 | 
			
		||||
  (save-excursion
 | 
			
		||||
    (goto-char (point-min))
 | 
			
		||||
    (while (not (eobp))
 | 
			
		||||
      (back-to-indentation)
 | 
			
		||||
      (let ((indent (- (point) (pos-bol))))
 | 
			
		||||
        (when (> indent count)
 | 
			
		||||
          (delete-char (- count))))
 | 
			
		||||
      (forward-line))))
 | 
			
		||||
 | 
			
		||||
(defun jshell--indent-line-function ()
 | 
			
		||||
  "`indent-line-function' for jshell comint buffers."
 | 
			
		||||
  (let* ((start (process-mark (get-buffer-process (current-buffer)))))
 | 
			
		||||
    ;; don't indent the first line
 | 
			
		||||
    (unless (= (pos-bol) (save-excursion (goto-char start) (pos-bol)))
 | 
			
		||||
      (let* ((input (buffer-substring-no-properties start (pos-eol)))
 | 
			
		||||
             (prompt-size (let ((bound (jshell--bounds-of-last-prompt)))
 | 
			
		||||
                            (- (cdr bound) (car bound))))
 | 
			
		||||
             (col (jshell--with-font-lock-buffer
 | 
			
		||||
                   (insert input)
 | 
			
		||||
                   (jshell--remove-extra-indentation prompt-size)
 | 
			
		||||
                   (c-indent-line nil t)
 | 
			
		||||
                   (back-to-indentation)
 | 
			
		||||
                   (- (point) (pos-bol)))))
 | 
			
		||||
        (save-excursion
 | 
			
		||||
          (indent-line-to (+ prompt-size col)))
 | 
			
		||||
        (skip-syntax-forward "-")))))
 | 
			
		||||
 | 
			
		||||
(defun jshell-send-input ()
 | 
			
		||||
  "Like `comint-send-input', but with some extra stuff for jshell."
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (let ((pmark (process-mark (get-buffer-process (current-buffer))))
 | 
			
		||||
        (end (if comint-eol-on-send (pos-eol) (point))))
 | 
			
		||||
    (with-restriction pmark end
 | 
			
		||||
      (let ((res (syntax-ppss (point-max))))
 | 
			
		||||
        (without-restriction
 | 
			
		||||
          (cond
 | 
			
		||||
           ;; open string
 | 
			
		||||
           ((cl-fourth res)
 | 
			
		||||
            (message "Unterminated string"))
 | 
			
		||||
           ;; unmatched blocks or comment
 | 
			
		||||
           ((or (numberp (cl-fifth res))
 | 
			
		||||
                (not (zerop (cl-first res)))
 | 
			
		||||
                ;; trailing . character
 | 
			
		||||
                (save-excursion
 | 
			
		||||
                  (end-of-line)
 | 
			
		||||
                  (skip-syntax-backward "-")
 | 
			
		||||
                  (eql (char-before) ?.)))
 | 
			
		||||
            (newline-and-indent))
 | 
			
		||||
           (t
 | 
			
		||||
            ;; jshell will echo out each line we send it, ignore all of them
 | 
			
		||||
            (setq-local jshell--skip-next-lines (count-lines pmark end))
 | 
			
		||||
            (when (= pmark end)
 | 
			
		||||
              (cl-incf jshell--skip-next-lines))
 | 
			
		||||
            ;; also, methods add a bunch of extra newlines
 | 
			
		||||
            (when (>= jshell--skip-next-lines 2)
 | 
			
		||||
              (cl-incf jshell--skip-next-lines (- jshell--skip-next-lines 2)))
 | 
			
		||||
            (comint-send-input))))))))
 | 
			
		||||
 | 
			
		||||
(defvar-keymap inferior-jshell-mode-map
 | 
			
		||||
  :doc "Keymap for `inferior-jshell-mode'."
 | 
			
		||||
  :parent comint-mode-map
 | 
			
		||||
  "RET" #'jshell-send-input)
 | 
			
		||||
 | 
			
		||||
(defvar inferior-jshell-mode-syntax-table (copy-syntax-table java-mode-syntax-table)
 | 
			
		||||
  "Syntax table for `inferior-jshell-mode'.")
 | 
			
		||||
 | 
			
		||||
(defun jshell--kill-fontification-buffer ()
 | 
			
		||||
  "Kill the current `jshell--fontification-buffer'."
 | 
			
		||||
  (kill-buffer jshell--fontification-buffer))
 | 
			
		||||
 | 
			
		||||
(define-derived-mode inferior-jshell-mode comint-mode "Inferior Jshell"
 | 
			
		||||
  "Major mode for buffers running inferior CERN jshell processes."
 | 
			
		||||
  :group 'inferior-jshell
 | 
			
		||||
  :syntax-table inferior-jshell-mode-syntax-table
 | 
			
		||||
  (setq-local comint-highlight-input nil
 | 
			
		||||
              indent-line-function #'jshell--indent-line-function
 | 
			
		||||
              electric-indent-chars '(?\n ?}))
 | 
			
		||||
  (add-hook 'comint-preoutput-filter-functions
 | 
			
		||||
            #'jshell--preoutput-filter-function
 | 
			
		||||
            nil t)
 | 
			
		||||
  (add-hook 'post-command-hook
 | 
			
		||||
            #'jshell--fontify-current-input
 | 
			
		||||
            nil t)
 | 
			
		||||
  (add-hook 'kill-buffer-hook
 | 
			
		||||
            #'jshell--kill-fontification-buffer
 | 
			
		||||
            nil t))
 | 
			
		||||
 | 
			
		||||
(cl-defun run-jshell (&optional (cmd jshell-program) (switches jshell-switches))
 | 
			
		||||
  "Run CERN jshell in a comint buffer.
 | 
			
		||||
CMD is the shell command to run.  It defaults to `jshell-program'.  SWITCHES are
 | 
			
		||||
the arguments to pass.  They default to `jshell-switches'."
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (pop-to-buffer
 | 
			
		||||
   (with-current-buffer (get-buffer-create jshell-buffer-name)
 | 
			
		||||
     (prog1 (current-buffer)
 | 
			
		||||
       (unless (process-live-p (get-buffer-process (current-buffer)))
 | 
			
		||||
         (inferior-jshell-mode)
 | 
			
		||||
         (comint-exec (current-buffer) "Inferior Jshell" cmd nil switches))))))
 | 
			
		||||
 | 
			
		||||
(cl-defun jshell--find-buffer ()
 | 
			
		||||
  "Find and return a live jshell buffer."
 | 
			
		||||
  (dolist (buffer (buffer-list))
 | 
			
		||||
    (with-current-buffer buffer
 | 
			
		||||
      (when (and (derived-mode-p 'inferior-jshell-mode)
 | 
			
		||||
                 (process-live-p (get-buffer-process buffer)))
 | 
			
		||||
        (cl-return-from jshell--find-buffer buffer)))))
 | 
			
		||||
 | 
			
		||||
(defun jshell-eval (code)
 | 
			
		||||
  "Evaluate CODE in a live JShell buffer."
 | 
			
		||||
  (interactive "sEval: ")
 | 
			
		||||
  (let ((buffer (jshell--find-buffer)))
 | 
			
		||||
    (unless buffer
 | 
			
		||||
      (user-error "No live JShell buffer found"))
 | 
			
		||||
    (with-current-buffer buffer
 | 
			
		||||
      (let* ((start (process-mark (get-buffer-process buffer)))
 | 
			
		||||
             (end (point-max))
 | 
			
		||||
             (old (buffer-substring-no-properties start end)))
 | 
			
		||||
        (delete-region start end)
 | 
			
		||||
        (goto-char (point-max))
 | 
			
		||||
        (insert code)
 | 
			
		||||
        (goto-char (point-max))
 | 
			
		||||
        (jshell-send-input)
 | 
			
		||||
        (goto-char (point-max))
 | 
			
		||||
        (insert old)
 | 
			
		||||
        (goto-char (point-max))))))
 | 
			
		||||
 | 
			
		||||
(defun jshell-eval-region (start end)
 | 
			
		||||
  "Evaluate the current buffer from START to END in a live JShell buffer.
 | 
			
		||||
START and END default to the current region."
 | 
			
		||||
  (interactive "r")
 | 
			
		||||
  (jshell-eval (buffer-substring-no-properties start end)))
 | 
			
		||||
 | 
			
		||||
(defun jshell-eval-buffer ()
 | 
			
		||||
  "Send the current buffer to a live JShell buffer."
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (jshell-eval-region (point-min) (point-max)))
 | 
			
		||||
 | 
			
		||||
(defun jshell-eval-defun ()
 | 
			
		||||
  "Send the defun under point to a live JShell buffer."
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (let ((bounds (bounds-of-thing-at-point 'defun)))
 | 
			
		||||
    (unless bounds
 | 
			
		||||
      (user-error "No defun under point"))
 | 
			
		||||
    (jshell-eval-region (car bounds) (cdr bounds))))
 | 
			
		||||
 | 
			
		||||
(defun jshell-eval-expression ()
 | 
			
		||||
  "Send the Java expression under point to a live JShell buffer.
 | 
			
		||||
This only works in `java-ts-mode'."
 | 
			
		||||
  (interactive)
 | 
			
		||||
  (let ((root (treesit-buffer-root-node)))
 | 
			
		||||
    (let ((node (car (or (treesit-query-range
 | 
			
		||||
                          root '([(expression_statement)
 | 
			
		||||
                                  (field_declaration)
 | 
			
		||||
                                  (local_variable_declaration)
 | 
			
		||||
                                  (import_declaration)]
 | 
			
		||||
                                 @exp)
 | 
			
		||||
                          (point) (1+ (point)))
 | 
			
		||||
                         (treesit-query-range
 | 
			
		||||
                          root '([(parenthesized_expression)
 | 
			
		||||
                                  (binary_expression)
 | 
			
		||||
                                  (update_expression)
 | 
			
		||||
                                  (unary_expression)]
 | 
			
		||||
                                 @exp)
 | 
			
		||||
                          (point) (1+ (point)))))))
 | 
			
		||||
      (unless node
 | 
			
		||||
        (user-error "No expression found under point"))
 | 
			
		||||
      (let ((text (buffer-substring-no-properties (car node) (cdr node))))
 | 
			
		||||
        (when (string-match (rx (* (syntax whitespace))
 | 
			
		||||
                                ";"
 | 
			
		||||
                                (* (syntax whitespace)) eos)
 | 
			
		||||
                            text)
 | 
			
		||||
          (setq text (substring text 0 (match-beginning 0))))
 | 
			
		||||
        (when (string-match (rx bos (* (syntax whitespace)) "("
 | 
			
		||||
                                (group (* any))
 | 
			
		||||
                                ")" (* (syntax whitespace)) eos)
 | 
			
		||||
                            text)
 | 
			
		||||
          (setq text (match-string 1 text)))
 | 
			
		||||
        (jshell-eval text)))))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
(provide 'inferior-jshell)
 | 
			
		||||
;;; inferior-jshell.el ends here
 | 
			
		||||
							
								
								
									
										6
									
								
								init.el
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								init.el
									
									
									
									
									
								
							@ -1765,10 +1765,16 @@ otherwise, call `bibtex-find-text'."
 | 
			
		||||
;; My dev environment for ROS2
 | 
			
		||||
(require 'arch-ros2)
 | 
			
		||||
 | 
			
		||||
(require 'inferior-jshell)
 | 
			
		||||
;; java-ts-mode
 | 
			
		||||
(use-package java-ts-mode
 | 
			
		||||
  :hook ((java-ts-mode . trusted-files-eglot-ensure-if-safe)
 | 
			
		||||
         (java-ts-mode . my/-setup-java-ts-mode))
 | 
			
		||||
  :bind (:map java-ts-mode-map
 | 
			
		||||
         ("C-c C-e" . jshell-eval-expression)
 | 
			
		||||
         ("C-M-x" . jshell-eval-defun)
 | 
			
		||||
         ("C-c C-r" . jshell-eval-region)
 | 
			
		||||
         ("C-c C-b" . jshell-eval-buffer))
 | 
			
		||||
  :config
 | 
			
		||||
  (defun my/-setup-java-ts-mode ()
 | 
			
		||||
    (let ((rules (car treesit-simple-indent-rules)))
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user