diff --git a/lsp-inline-completion.el b/lsp-inline-completion.el new file mode 100644 index 00000000000..fffda85bb12 --- /dev/null +++ b/lsp-inline-completion.el @@ -0,0 +1,550 @@ +;;; lsp-inline-completion.el --- LSP mode -*- lexical-binding: t; -*- + +;; Copyright (C) 2020-2024 emacs-lsp maintainers + +;; Author: Rodrigo Kassick +;; Keywords: languages +;; Package-Requires: ((emacs "27.1") (dash "2.18.0") (spinner "1.7.3")) +;; Version: 9.0.1 + +;; URL: https://github.com/emacs-lsp/lsp-mode +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Inline Completions support +;; Specification here https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#textDocument_inlineCompletion + +;;; Code: + +(require 'lsp-protocol) +(require 'dash) +(require 'cl-lib) +(require 'fringe) + +(if (version< emacs-version "29.1") + ;; Undo macro probably introduced in 29.1 + (defmacro lsp-inline-completion--with-undo-amalgamate (&rest body) + "Like `progn' but perform BODY with amalgamated undo barriers. + +This allows multiple operations to be undone in a single step. +When undo is disabled this behaves like `progn'." + (declare (indent 0) (debug t)) + (let ((handle (make-symbol "--change-group-handle--"))) + `(let ((,handle (prepare-change-group)) + ;; Don't truncate any undo data in the middle of this, + ;; otherwise Emacs might truncate part of the resulting + ;; undo step: we want to mimic the behavior we'd get if the + ;; undo-boundaries were never added in the first place. + (undo-outer-limit nil) + (undo-limit most-positive-fixnum) + (undo-strong-limit most-positive-fixnum)) + (unwind-protect + (progn + (activate-change-group ,handle) + ,@body) + (progn + (accept-change-group ,handle) + (undo-amalgamate-change-group ,handle)))))) + (defalias 'lsp-inline-completion--with-undo-amalgamate 'with-undo-amalgamate)) + +(defun lsp-inline-completion--params (implicit &optional identifier position) + "Returns a InlineCompletionParams instance" + (lsp-make-inline-completion-params + :textDocument (or identifier (lsp--text-document-identifier)) + :position (or position (lsp--cur-position)) + :context (lsp-make-inline-completion-context + :triggerKind (if implicit + lsp/inline-completion-trigger-automatic + lsp/inline-completion-trigger-invoked)))) + +(defun lsp-inline-completion--parse-items (response) + "Parses the reponse from the server and returns a list of +InlineCompletionItem objects" + + (pcase response + ;; Server responded with a completion list + ((lsp-interface InlineCompletionList :items) + (seq-into items 'list)) + + ;; Server responded with a sequence of completion items + ((pred (lambda (i) + (and (sequencep i) + (lsp-inline-completion-item? (elt i 0))))) + (seq-into response 'list)) + + ;; A sequence means multiple server may have responded. Iterate over them and normalize + ((pred sequencep) + (let ((item-seq (cl-map 'list #'lsp-inline-completion--parse-items response))) + (apply 'seq-concatenate `(list ,@item-seq)))))) + +;;;;;; Default UI -- overlay + +;;;###autoload +(defvar lsp-inline-completion-active-map + (let ((map (make-sparse-keymap))) + ;; accept + (define-key map (kbd "C-") #'lsp-inline-completion-accept) + (define-key map [mouse-1] #'lsp-inline-completion-accept-on-click) + ;; navigate + (define-key map (kbd "C-n") #'lsp-inline-completion-next) + (define-key map (kbd "C-p") #'lsp-inline-completion-prev) + ;; cancel + (define-key map (kbd "C-g") #'lsp-inline-completion-cancel) + (define-key map (kbd "") #'lsp-inline-completion-cancel) + (define-key map (kbd "C-c C-k") #'lsp-inline-completion-cancel) + ;; useful -- recenter without loosing the completion + (define-key map (kbd "C-l") #'recenter-top-bottom) + ;; ignore + (define-key map [down-mouse-1] #'ignore) + (define-key map [up-mouse-1] #'ignore) + (define-key map [mouse-movement] #'ignore) + ;; Any event outside of the map, cancel and use it + (define-key map [t] #'lsp-inline-completion-cancel-with-input) + map) + "Keymap active when showing inline code suggestions") + +;;;###autoload +(defface lsp-inline-completion-overlay-face + '((t :inherit shadow)) + "Face for the inline code suggestions overlay." + :group 'lsp-mode) + +;; Local Buffer State + +(defvar-local lsp-inline-completion--items nil "The completions provided by the server") +(defvar-local lsp-inline-completion--current nil "The current suggestion to be displayed") +(defvar-local lsp-inline-completion--overlay nil "The overlay displaying code suggestions") +(defvar-local lsp-inline-completion--start-point nil "The point where the completion started") + +(defcustom lsp-before-inline-completion-hook nil + "Hooks run before starting code suggestions" + :type 'hook + :group 'lsp-mode) + +(defcustom lsp-after-inline-completion-hook nil + "Hooks executed after asking for code suggestions." + :type 'hook + :group 'lsp-mode) + +(defcustom lsp-inline-completion-accepted-hook nil + "Hooks executed after accepting a code suggestion. The hooks receive the +text range that was updated by the completion" + :type 'hook + :group 'lsp-mode) + +(defcustom lsp-inline-completion-cancelled-hook nil + "Hooks executed after cancelling the completion UI" + :type 'hook + :group 'lsp-mode) + +(defcustom lsp-inline-completion-before-show-hook nil + "Hooks executed before showing a suggestion." + :type 'hook + :group 'lsp-mode) + +(defcustom lsp-inline-completion-shown-hook nil + "Hooks executed after showing a suggestion." + :type 'hook + :group 'lsp-mode) + +(defcustom lsp-inline-completion-overlay-priority 9000 + "The priority of the overlay." + :type '(choice (const :tag "No Priority" nil) + (integer :tag "Simple, Overriding Priority") + (cons :tag "Composite" + (choice (integer :tag "Primary") + (const :tag "Primary Unset" nil)) + (integer :tag "Secondary"))) + :group 'lsp-mode) + +(defsubst lsp-inline-completion--overlay-visible () + "Return whether the `overlay' is avaiable." + (and (overlayp lsp-inline-completion--overlay) + (overlay-buffer lsp-inline-completion--overlay))) + +(defun lsp-inline-completion--clear-overlay () + "Hide the suggestion overlay" + (when (lsp-inline-completion--overlay-visible) + (delete-overlay lsp-inline-completion--overlay)) + (setq lsp-inline-completion--overlay nil)) + + +(defun lsp-inline-completion--get-overlay (beg end) + "Build the suggestions overlay" + (when (overlayp lsp-inline-completion--overlay) + (lsp-inline-completion--clear-overlay)) + + (setq lsp-inline-completion--overlay (make-overlay beg end nil nil t)) + (overlay-put lsp-inline-completion--overlay 'keymap lsp-inline-completion-active-map) + (overlay-put lsp-inline-completion--overlay 'priority lsp-inline-completion-overlay-priority) + + lsp-inline-completion--overlay) + + +(defun lsp-inline-completion--show-keys () + "Shows active keymap hints in the minibuffer" + + (unless (and lsp-inline-completion--items + (numberp lsp-inline-completion--current)) + (error "No completions to show")) + + (let ((message-log-max nil)) + (message (concat "Completion " + (propertize (format "%d" (1+ lsp-inline-completion--current)) 'face 'bold) + "/" + (propertize (format "%d" (length lsp-inline-completion--items)) 'face 'bold) + + (-when-let (keys (where-is-internal #'lsp-inline-completion-next lsp-inline-completion-active-map)) + (concat ". " + (propertize " Next" 'face 'italic) + (format ": [%s]" + (string-join (--map (propertize (key-description it) 'face 'help-key-binding) + keys) + "/")))) + (-when-let (keys (where-is-internal #'lsp-inline-completion-accept lsp-inline-completion-active-map)) + (concat (propertize " Accept" 'face 'italic) + (format ": [%s]" + (string-join (--map (propertize (key-description it) 'face 'help-key-binding) + keys) + "/")))))))) + +(defun lsp-inline-completion-show-overlay () + "Makes the suggestion overlay visible" + (unless (and lsp-inline-completion--items + (numberp lsp-inline-completion--current)) + (error "No completions to show")) + + (lsp-inline-completion--clear-overlay) + + (run-hooks 'lsp-inline-completion-before-show-hook) + + (-let* ((suggestion + (elt lsp-inline-completion--items + lsp-inline-completion--current)) + ((&InlineCompletionItem? :insert-text :range?) suggestion) + ((&RangeToPoint :start :end) range?) + (start-point (or start (point))) + (showing-at-eol (save-excursion + (goto-char start-point) + (eolp))) + (beg (if showing-at-eol (1- start-point) start-point)) + (end-point (or end (1+ beg))) + (text (cond + ((lsp-markup-content? insert-text) (lsp:markup-content-value insert-text)) + (t insert-text))) + (propertizedText (concat + (buffer-substring beg start-point) + (propertize text 'face 'lsp-inline-completion-overlay-face))) + (ov (lsp-inline-completion--get-overlay beg end-point)) + (completion-is-substr (string-equal + (buffer-substring beg lsp-inline-completion--start-point) + (substring propertizedText 0 (- lsp-inline-completion--start-point beg)))) + display-str after-str target-position) + + (goto-char beg) + + (put-text-property 0 (length propertizedText) 'cursor t propertizedText) + + (if completion-is-substr + (progn + ;; Show the prefix as `display' + (setq display-str (substring propertizedText 0 (- lsp-inline-completion--start-point beg))) + (setq after-str (substring propertizedText (- lsp-inline-completion--start-point beg) nil)) + (setq target-position lsp-inline-completion--start-point)) + + + (setq display-str (substring propertizedText 0 1)) + (setq after-str (substring propertizedText 1)) + (setq target-position beg)) + + (overlay-put ov 'display display-str) + (overlay-put ov 'after-string after-str) + + (goto-char target-position) + + (lsp-inline-completion--show-keys) + (run-hooks 'lsp-inline-completion-shown-hook))) + +(defun lsp-inline-completion--insert-sugestion (text kind start end command?) + (let* ((text-insert-start (or start lsp-inline-completion--start-point)) + text-insert-end + (completion-is-substr (string-equal + (buffer-substring text-insert-start lsp-inline-completion--start-point) + (substring text 0 (- lsp-inline-completion--start-point text-insert-start))))) + (when text-insert-start + (goto-char text-insert-start)) + + ;; When range is provided, must replace the text of the range by the text + ;; to insert + (when (and start end (/= start end)) + (delete-region start end)) + + ;; Insert suggestion, keeping the cursor at the start point + (insert text) + + (setq text-insert-end (point)) + + ;; If a template, format it -- keep track of the end position! + (when (eq kind 'snippet) + (let ((end-marker (set-marker (make-marker) (point)))) + (lsp--expand-snippet (buffer-substring text-insert-start text-insert-end) + text-insert-start + text-insert-end) + (setq text-insert-end (marker-position end-marker)) + (set-marker end-marker nil))) + + ;; Post command + (when command? + (lsp--execute-command command?)) + + (if completion-is-substr + (goto-char lsp-inline-completion--start-point) + (goto-char text-insert-start)) + + ;; hooks + (run-hook-with-args-until-failure 'lsp-inline-completion-accepted-hook text text-insert-start text-insert-end))) + +(defun lsp-inline-completion-accept () + "Accepts the current suggestion" + (interactive) + (unless (lsp-inline-completion--overlay-visible) + (error "Not showing suggestions")) + + (lsp-inline-completion--clear-overlay) + (-let* ((suggestion (elt lsp-inline-completion--items lsp-inline-completion--current)) + ((&InlineCompletionItem? :insert-text :range? :command?) suggestion) + ((kind . text) (cond + ((lsp-markup-content? insert-text) + (cons 'snippet (lsp:markup-content-value insert-text) )) + (t (cons 'text insert-text)))) + ((start . end) (when range? + (-let (((&RangeToPoint :start :end) range?)) (cons start end))))) + + (with-no-warnings + ;; Compiler does not believes this macro is defined + (lsp-inline-completion--with-undo-amalgamate + (lsp-inline-completion--insert-sugestion text kind start end command?))))) + +(defun lsp-inline-completion-accept-on-click (event) + (interactive "e") + + (lsp-inline-completion-accept) + (-let (((col . row) (posn-actual-col-row (event-end event)))) + (move-to-window-line row) + (beginning-of-line) + (forward-char (- col + (if (bound-and-true-p display-line-numbers-mode) + (+ 2 (line-number-display-width)) + 0))))) + +(defun lsp-inline-completion-cancel () + "Close the suggestion overlay" + (interactive) + (when (lsp-inline-completion--overlay-visible) + + (lsp-inline-completion--clear-overlay) + + (when lsp-inline-completion--start-point + (goto-char lsp-inline-completion--start-point)) + + (run-hooks 'lsp-inline-completion-cancelled-hook))) + +(defun lsp-inline-completion-cancel-with-input (event &optional arg) + "Cancel the inline completion and executes whatever event was received" + (interactive (list last-input-event current-prefix-arg)) + + (lsp-inline-completion-cancel) + + (let ((command (lookup-key (current-active-maps) (vector event))) + (current-prefix-arg arg)) + + (when (commandp command) + (call-interactively command)))) + +(defun lsp-inline-completion-next () + "Display the next inline completion" + (interactive) + (unless (lsp-inline-completion--overlay-visible) + (error "Not showing suggestions")) + (setq lsp-inline-completion--current + (mod (1+ lsp-inline-completion--current) + (length lsp-inline-completion--items))) + + (lsp-inline-completion-show-overlay)) + +(defun lsp-inline-completion-prev () + "Display the previous inline completion" + (interactive) + (unless (lsp-inline-completion--overlay-visible) + (error "Not showing suggestions")) + (setq lsp-inline-completion--current + (mod (1- lsp-inline-completion--current) + (length lsp-inline-completion--items))) + + (lsp-inline-completion-show-overlay)) + +;;;###autoload +(defun lsp-inline-completion-display (&optional implicit) + "Displays the inline completions overlay" + (interactive) + + (unless implicit + (lsp--spinner-start) ) + + (unwind-protect + (if-let* ((resp (lsp-request-while-no-input "textDocument/inlineCompletion" + (lsp-inline-completion--params implicit))) + (items (lsp-inline-completion--parse-items resp))) + + (progn + (lsp-inline-completion--clear-overlay) + (setq lsp-inline-completion--items items) + (setq lsp-inline-completion--current 0) + (setq lsp-inline-completion--start-point (point)) + (lsp-inline-completion-show-overlay)) + (unless implicit + (lsp--info "No Suggestions!"))) + ;; Clean up + (unless implicit + (lsp--spinner-stop)))) + + +;; Inline Completion Mode +(defcustom lsp-inline-completion-enable t + "If non-nil it will enable inline completions on idle." + :type 'boolean + :group 'lsp-mode + :package-version '(lsp-mode . "9.0.1")) + +(defcustom lsp-inline-completion-idle-delay 2 + "The number of seconds before trying to fetch inline completions, when +lsp-inline-completion-mode is active" + :type 'number + :group 'lsp-mode + :package-version '(lsp-mode . "9.0.1")) + +(defcustom lsp-inline-completion-inhibit-predicates nil + "When a function of this list returns non nil, lsp-inline-completion-mode will not show the completion" + :type '(repeat function) + :group 'lsp-mode) + +(defvar-local lsp-inline-completion--idle-timer nil + "The idle timer used by lsp-inline-completion-mode") + +;;;###autoload +(define-minor-mode lsp-inline-completion-mode + "Mode automatically displaying inline completions." + :lighter nil + (cond + ((and lsp-inline-completion-mode lsp--buffer-workspaces) + (add-hook 'lsp-on-change-hook #'lsp-inline-completion--after-change nil t)) + (t + (when lsp-inline-completion--idle-timer + (cancel-timer lsp-inline-completion--idle-timer)) + + (lsp-inline-completion-cancel) + + (remove-hook 'lsp-on-change-hook #'lsp-inline-completion--after-change t)))) + +(defun lsp-inline-completion--maybe-display (original-buffer original-point) + ;; This is executed on an idle timer -- ensure state did not change before + ;; displaying + (when (and (buffer-live-p original-buffer) + (eq (current-buffer) original-buffer) + (eq (point) original-point) + (--none? (funcall it) lsp-inline-completion-inhibit-predicates)) + (setq last-command this-command) + (setq this-command 'lsp-inline-completion-display) + (lsp-inline-completion-display 'implicit))) + +(defun lsp-inline-completion--after-change (&rest _) + ;; This function is in lsp-on-change-hooks, which is executed on a timer by + ;; lsp-on-change. Do not assume that the buffer/window state has not been + ;; modified in the meantime! Use the values in lsp--after-change-vals to + ;; ensure this. + + (when lsp-inline-completion--idle-timer + (cancel-timer lsp-inline-completion--idle-timer)) + + (when (and lsp-inline-completion-mode lsp--buffer-workspaces) + (let ((original-buffer (plist-get lsp--after-change-vals :buffer)) + (original-point (plist-get lsp--after-change-vals :point))) + (setq lsp-inline-completion--idle-timer + (run-with-idle-timer lsp-inline-completion-idle-delay + nil + #'lsp-inline-completion--maybe-display + original-buffer + original-point))))) + +;;;###autoload +(add-hook 'lsp-configure-hook (lambda () + (when (and lsp-inline-completion-enable + (lsp-feature? "textDocument/inlineCompletion")) + (lsp-inline-completion-mode)))) + +;; Company default integration + +(declare-function company--active-p "ext:company") +(declare-function company-cancel "ext:company" (&optional result)) +(declare-function company-manual-begin "ext:company") +(defvar company--begin-inhibit-commands) +(defcustom lsp-inline-completion-mode-inhibit-when-company-active t + "If the inline completion mode should avoid calling completions when company is active" + :type 'boolean + :group 'lsp-mode) + +(defvar-local lsp-inline-completion--showing-company nil "If company was active when the tooltip is shown") + +(defun lsp-inline-completion--company-save-state-and-hide () + (setq lsp-inline-completion--showing-company + (and (bound-and-true-p company-mode) + (company--active-p))) + + (when lsp-inline-completion--showing-company + (company-cancel))) + +(defun lsp-inline-completion--company-restore-state () + (when lsp-inline-completion--showing-company + (company-manual-begin)) + (setq lsp-inline-completion--showing-company nil)) + +(defun lsp-inline-completion--company-active-p () + (and (bound-and-true-p company-mode) (company--active-p))) + +;;;###autoload +(define-minor-mode lsp-inline-completion-company-integration-mode + "Minor mode to be used when company mode is active with lsp-inline-completion-mode" + :lighter nil + (cond + ((and lsp-inline-completion-company-integration-mode lsp--buffer-workspaces (bound-and-true-p company-mode)) + (add-hook 'lsp-inline-completion-before-show-hook #'lsp-inline-completion--company-save-state-and-hide nil t) + (add-hook 'lsp-inline-completion-cancelled-hook #'lsp-inline-completion--company-restore-state nil t) + (unless (memq #'lsp-inline-completion-display company--begin-inhibit-commands) + (setq-local company--begin-inhibit-commands + (cons #'lsp-inline-completion-display company--begin-inhibit-commands))) + (when (and lsp-inline-completion-mode-inhibit-when-company-active + (not (memq #'lsp-inline-completion--company-active-p lsp-inline-completion-inhibit-predicates))) + (setq-local lsp-inline-completion-inhibit-predicates + (cons #'lsp-inline-completion--company-active-p lsp-inline-completion-inhibit-predicates)))) + + (t + (remove-hook 'lsp-inline-completion-before-show-hook #'lsp-inline-completion--company-save-state-and-hide t) + (remove-hook 'lsp-inline-completion-cancelled-hook #'lsp-inline-completion--company-save-state-and-hide t) + (when (boundp 'company--begin-inhibit-commands) + (setq-local company--begin-inhibit-commands (delq #'lsp-inline-completion-display company--begin-inhibit-commands))) + (setq-local lsp-inline-completion-inhibit-predicates + (delq #'lsp-inline-completion--company-active-p lsp-inline-completion-inhibit-predicates))))) + +(provide 'lsp-inline-completion) diff --git a/lsp-mode.el b/lsp-mode.el index 5f81502ec9b..3bb4478a90a 100644 --- a/lsp-mode.el +++ b/lsp-mode.el @@ -1010,6 +1010,7 @@ directory") (with-lsp-workspace wk (lsp:completion-options-resolve-provider? (lsp--capability-for-method "textDocument/completion"))))) + ("textDocument/inlineCompletion" :capability :inlineCompletionProvider) ("textDocument/declaration" :capability :declarationProvider) ("textDocument/definition" :capability :definitionProvider) ("textDocument/documentColor" :capability :colorProvider) @@ -4949,6 +4950,12 @@ Added to `after-change-functions'." lsp-managed-mode) (run-hooks 'lsp-on-change-hook))) + +(defvar-local lsp--after-change-vals nil + "plist that stores the buffer state when `lsp--after-change' has ben activated. Since the +functions in `lsp-on-change-hook' are called with a timer, mouse +movements may have changed the position") + (defun lsp--after-change (buffer) "Called after most textDocument/didChange events." (setq lsp--signature-last-index nil @@ -4964,6 +4971,9 @@ Added to `after-change-functions'." (lsp--semantic-tokens-refresh-if-enabled buffer)) (when lsp--on-change-timer (cancel-timer lsp--on-change-timer)) + + (setq lsp--after-change-vals (list :point (point) + :buffer (current-buffer))) (setq lsp--on-change-timer (run-with-idle-timer lsp-idle-delay nil @@ -5429,9 +5439,9 @@ If EXCLUDE-DECLARATION is non-nil, request the server to include declarations." (lsp-help-mode) (with-help-window lsp-help-buf-name (insert - (mapconcat 'string-trim-right - (split-string (lsp--render-on-hover-content contents t) "\n") - "\n")))) + (mapconcat 'string-trim-right + (split-string (lsp--render-on-hover-content contents t) "\n") + "\n")))) (run-mode-hooks))) (lsp--info "No content at point.")))) @@ -6287,7 +6297,10 @@ A reference is highlighted only if it is visible in a window." (let* ((wins-visible-pos (-map (lambda (win) (cons (1- (line-number-at-pos (window-start win) t)) - (1+ (line-number-at-pos (window-end win) t)))) + (1+ (line-number-at-pos (min (window-end win) + (with-current-buffer (window-buffer win) + (buffer-end +1))) + t)))) (get-buffer-window-list nil nil 'visible)))) (setq lsp--have-document-highlights t) (-map diff --git a/lsp-protocol.el b/lsp-protocol.el index f1d8fd90967..3b39db0e210 100644 --- a/lsp-protocol.el +++ b/lsp-protocol.el @@ -500,6 +500,8 @@ See `-let' for a description of the destructuring mechanism." (defconst lsp/completion-trigger-kind-invoked 1) (defconst lsp/completion-trigger-kind-trigger-character 2) (defconst lsp/completion-trigger-kind-trigger-for-incomplete-completions 3) +(defconst lsp/inline-completion-trigger-invoked 1 "Explicit invocation as per https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#inlineCompletionTriggerKind") +(defconst lsp/inline-completion-trigger-automatic 2 "Automatic invocation as per https://microsoft.github.io/language-server-protocol/specifications/lsp/3.18/specification/#inlineCompletionTriggerKind") (defvar lsp/diagnostic-severity-lookup [nil Error Warning Information Hint Max]) (defconst lsp/diagnostic-severity-error 1) @@ -819,7 +821,12 @@ See `-let' for a description of the destructuring mechanism." ;; 3.17 (InlayHint (:label :position) (:kind :paddingLeft :paddingRight)) (InlayHintLabelPart (:value) (:tooltip :location :command)) - (InlayHintsParams (:textDocument) (:range))) + (InlayHintsParams (:textDocument) (:range)) + ;; 3.18 + (InlineCompletionParams (:textDocument :position :context)) + (InlineCompletionContext (:triggerKind)) + (InlineCompletionItem (:insertText) (:filterText :range :command)) + (InlineCompletionList (:items) nil)) ;; 3.17 (defconst lsp/inlay-hint-kind-type-hint 1)