branch: externals/phpinspect commit 6784c90d5665aa6ee16ce4d697bb25e0f7647715 Author: Hugo Thunnissen <de...@hugot.nl> Commit: Hugo Thunnissen <de...@hugot.nl>
Shadow buffer in semi usable state --- phpinspect-buffer.el | 332 +++++++++++++++++++++++++++-------------- phpinspect-completion.el | 49 +++--- phpinspect-diagnose.el | 1 - phpinspect-parser.el | 80 +++++++++- phpinspect-pipeline.el | 2 +- phpinspect-token-predicates.el | 9 +- phpinspect-util.el | 6 +- test/test-buffer.el | 58 ++++--- test/test-parser.el | 5 + 9 files changed, 366 insertions(+), 176 deletions(-) diff --git a/phpinspect-buffer.el b/phpinspect-buffer.el index f7a9d1e6b0..7c52a0d100 100644 --- a/phpinspect-buffer.el +++ b/phpinspect-buffer.el @@ -226,7 +226,6 @@ tokens that have been deleted from a buffer." (unless (phpinspect-probably-token-p token) (error "%s does not seem to be a token" token)) - (phpinspect--log "Deleting index for token %s" token) (cond ((phpinspect-class-declaration-p token) (when-let ((typedef (gethash token (phpinspect-buffer-token-index buffer)))) (if-let ((name (seq-find #'phpinspect-word-p token)) @@ -237,9 +236,17 @@ tokens that have been deleted from a buffer." (progn ;; Declaration has been replaced and class name is unchanged. ;; Update existing typedef. - (phpinspect-buffer--index-class-declaration buffer new-declaration) - ;; Delete old index ref - (remhash token (phpinspect-buffer-token-index buffer))) + (if (equal (phpinspect-meta-token new-declaration) + token) + (progn + ;; tokens are equal, just update the index reference + (phpinspect-buffer-update-index-reference-for-token + buffer token (phpinspect-meta-token new-declaration))) + + ;; Tokens are different, update class declaration + (phpinspect-buffer--index-class-declaration buffer new-declaration) + ;; Delete old index ref + (remhash token (phpinspect-buffer-token-index buffer)))) (progn ;; Else: delete index ref AND associated typedef (remhash token (phpinspect-buffer-token-index buffer)) @@ -252,20 +259,49 @@ tokens that have been deleted from a buffer." (car var)))) (phpi-typedef-delete-property class (cdr var))))) ((phpinspect-function-p token) - (when-let ((func (gethash token (phpinspect-buffer-token-index buffer)))) - (remhash token (phpinspect-buffer-token-index buffer)) - (cond ((phpinspect-project-p (car func)) - (phpinspect-project-delete-function (phpinspect-buffer-project buffer) (phpinspect--function-name (cdr func)))) - ((phpinspect--type-p (car func)) - (when-let ((class (phpinspect-project-get-typedef - (phpinspect-buffer-project buffer) - (car func)))) - (phpi-typedef-delete-method class (cdr func)))) - (t (phpinspect-message "Invalid index location, reindexing buffer") - (phpinspect-buffer-reindex buffer) - (error "invalid index location"))))) + (phpinspect-buffer--delete-function-index-reference buffer token)) (t (error "Cannot delete index for token %s" token)))) +(defun phpinspect-buffer--delete-function-index-reference (buffer token) + (message "Deleting func %s" token) + (when-let ((func (gethash token (phpinspect-buffer-token-index buffer)))) + (let ((arg-list (phpinspect-function-argument-list token))) + (if-let ((arg-list-meta (phpinspect-buffer-token-meta buffer arg-list)) + ((eq (phpinspect-buffer-root-meta buffer) + (phpinspect-meta-find-root arg-list-meta))) + ;; Arg-list has been adopted into current metadata tree, check + ;; if declarations are equal. + (new-declaration (phpinspect-meta-find-parent-matching-token + arg-list-meta #'phpinspect-declaration-p)) + ((equal (phpinspect-meta-token new-declaration) + (phpinspect-function-declaration token))) + ((thread-last (phpinspect-meta-parent new-declaration) + (phpinspect-meta-token) + (phpinspect-function-p)))) + (progn + ;; Declaration is equal, update index reference + (message "updating index ref, %s was equal to %s" + (phpinspect-meta-token new-declaration) + (phpinspect-function-declaration token)) + (phpinspect-buffer-update-index-reference-for-token + buffer token (phpinspect-meta-token (phpinspect-meta-parent new-declaration)))) + + (progn + (message "Deleting index") + ;; Declaration is not equal, delete index + (remhash token (phpinspect-buffer-token-index buffer)) + (cond ((phpinspect-project-p (car func)) + (phpinspect-project-delete-function (phpinspect-buffer-project buffer) (phpinspect--function-name (cdr func)))) + ((phpinspect--type-p (car func)) + (message "Getting typedef for %s" (car func)) + (when-let ((class (phpinspect-project-get-typedef + (phpinspect-buffer-project buffer) + (car func)))) + (phpi-typedef-delete-method class (cdr func)))) + (t (phpinspect-message "Invalid index location, reindexing buffer") + (phpinspect-buffer-reindex buffer) + (error "invalid index location")))))))) + (cl-defmethod phpinspect-buffer-reset ((buffer phpinspect-buffer)) "Clear all metadata stored in BUFFER." (phpinspect-buffer--clear-query-cache buffer) @@ -369,9 +405,10 @@ tokens that have been deleted from a buffer." (type-resolver (phpinspect-buffer-get-type-resolver-for-class buffer class)) config) (dolist (use uses) - (setq config (nconc config - (thread-first (phpinspect-meta-token use) - (phpinspect--index-trait-use type-resolver nil))))) + (when-let ((part (thread-first (phpinspect-meta-token use) + (phpinspect--index-trait-use type-resolver nil)))) + (setq config (nconc config part)))) + config)) (defun phpinspect-buffer--index-trait-use (buffer trait-use) @@ -584,51 +621,53 @@ and a map is available from the previous parse, this is used. If none is available `phpinspect-buffer-parse' is called before continuing execution." - ;; Parse buffer if no map is available. - (unless (phpinspect-buffer-map buffer) - (phpinspect-buffer-parse buffer)) - - (phpinspect--log "Preparing to update project index") - ;; Use inhibit-quit to prevent index corruption though partial index - ;; application. - (let ((inhibit-quit t)) - (when (phpinspect-buffer-project buffer) - (let ((map (phpinspect-buffer-map buffer))) - (unless (eq map (phpinspect-buffer--last-indexed-bmap buffer)) - (phpinspect--log "Updating project index") - - ;; Process deleted tokens - (dolist (deletion (phpinspect-buffer--deletions buffer)) - (pcase (phpinspect-meta-token deletion) - ((pred phpinspect--can-delete-buffer-index-for-token) - (phpinspect-buffer-delete-index-for-token buffer (phpinspect-meta-token deletion))) - ((pred phpinspect-use-trait-p) - (when-let ((class (seq-find (phpinspect-meta-token-predicate #'phpinspect-class-p) - (phpinspect-buffer-tokens-enclosing-point - buffer (phpinspect-meta-start deletion)))) - (declaration (phpinspect-meta-find-first-child-matching-token - class #'phpinspect-class-declaration-p))) - (phpinspect-buffer--index-class-declaration buffer declaration 'force))))) - - ;; Process newly parsed tokens - (dolist (addition (phpinspect-buffer--additions buffer)) - (pcase (phpinspect-meta-token addition) - ((pred phpinspect-class-declaration-p) - (phpinspect-buffer--index-class-declaration buffer addition)) - ((pred phpinspect-function-p) - (phpinspect-buffer--index-function buffer addition)) - ((pred phpinspect-use-trait-p) - (phpinspect-buffer--index-trait-use buffer addition)) - ((pred phpinspect-this-p) - (phpinspect-buffer--index-this buffer addition)) - ((or (pred phpinspect-class-variable-p) - (pred phpinspect-const-p)) - (phpinspect-buffer--index-class-variable buffer addition)))) - - (setf (phpinspect-buffer--additions buffer) nil - (phpinspect-buffer--deletions buffer) nil)) - - (setf (phpinspect-buffer--last-indexed-bmap buffer) map))))) + (phpi-shadow-await-index-synced (phpinspect-buffer-shadow buffer) t)) + + ;; ;; Parse buffer if no map is available. + ;; (unless (phpinspect-buffer-map buffer) + ;; (phpinspect-buffer-parse buffer)) + + ;; (phpinspect--log "Preparing to update project index") + ;; ;; Use inhibit-quit to prevent index corruption though partial index + ;; ;; application. + ;; (let ((inhibit-quit t)) + ;; (when (phpinspect-buffer-project buffer) + ;; (let ((map (phpinspect-buffer-map buffer))) + ;; (unless (eq map (phpinspect-buffer--last-indexed-bmap buffer)) + ;; (phpinspect--log "Updating project index") + + ;; ;; Process deleted tokens + ;; (dolist (deletion (phpinspect-buffer--deletions buffer)) + ;; (pcase (phpinspect-meta-token deletion) + ;; ((pred phpinspect--can-delete-buffer-index-for-token) + ;; (phpinspect-buffer-delete-index-for-token buffer (phpinspect-meta-token deletion))) + ;; ((pred phpinspect-use-trait-p) + ;; (when-let ((class (seq-find (phpinspect-meta-token-predicate #'phpinspect-class-p) + ;; (phpinspect-buffer-tokens-enclosing-point + ;; buffer (phpinspect-meta-start deletion)))) + ;; (declaration (phpinspect-meta-find-first-child-matching-token + ;; class #'phpinspect-class-declaration-p))) + ;; (phpinspect-buffer--index-class-declaration buffer declaration 'force))))) + + ;; ;; Process newly parsed tokens + ;; (dolist (addition (phpinspect-buffer--additions buffer)) + ;; (pcase (phpinspect-meta-token addition) + ;; ((pred phpinspect-class-declaration-p) + ;; (phpinspect-buffer--index-class-declaration buffer addition)) + ;; ((pred phpinspect-function-p) + ;; (phpinspect-buffer--index-function buffer addition)) + ;; ((pred phpinspect-use-trait-p) + ;; (phpinspect-buffer--index-trait-use buffer addition)) + ;; ((pred phpinspect-this-p) + ;; (phpinspect-buffer--index-this buffer addition)) + ;; ((or (pred phpinspect-class-variable-p) + ;; (pred phpinspect-const-p)) + ;; (phpinspect-buffer--index-class-variable buffer addition)))) + + ;; (setf (phpinspect-buffer--additions buffer) nil + ;; (phpinspect-buffer--deletions buffer) nil)) + + ;; (setf (phpinspect-buffer--last-indexed-bmap buffer) map))))) (defun phpinspect-buffer-parse-map (buffer) (phpinspect-buffer-parse buffer) @@ -651,7 +690,6 @@ continuing execution." (setq start (- start (length (match-string 0)))) (setq pre-change-length (+ pre-change-length (length (match-string 0)))))) - ;;(message "Registering edit: %d, %d, %d" start end pre-change-length) (phpi-shadow-enqueue-task (phpinspect-buffer-shadow buffer) (phpi-change-create (phpinspect-buffer-buffer buffer) @@ -684,7 +722,7 @@ use." (phpinspect-meta-end meta))))) (defun phpinspect-buffer-root-meta (buffer) - (phpi-shadow-await-synced (phpinspect-buffer-shadow buffer)) + (phpi-shadow-await-synced (phpinspect-buffer-shadow buffer) t) (phpinspect-bmap-root-meta (phpinspect-buffer-map buffer))) (defun phpinspect-display-buffer-tree () @@ -760,6 +798,7 @@ If provided, PROJECT must be an instance of `phpinspect-project'." (cl-defstruct (phpinspect-shadow (:constructor phpinspect-make-shadow-generated) (:conc-name phpi-shadow-)) (synced-p t :type boolean) + (index-synced-p t :type boolean) (origin nil :type phpinspect-buffer) (buffer nil :type buffer) (queue nil :type phpinspect--queue) @@ -770,17 +809,17 @@ If provided, PROJECT must be an instance of `phpinspect-project'." (thread-signal (phpi-shadow-thread shadow) 'phpinspect-wakeup-shadow nil)) (defun phpi-shadow-thread-check-pause () - (ignore-error phpinspect-wakeup-shadow - (if (or (phpinspect--input-pending-p) - quit-flag) - (let* ((mx (make-mutex)) - (continue (make-condition-variable mx))) - (phpinspect-thread-pause phpinspect-shadow-pause-time mx continue)) - (thread-yield)))) + (if (or (phpinspect--input-pending-p) + quit-flag) + (let* ((mx (make-mutex)) + (continue (make-condition-variable mx))) + (phpinspect-thread-pause phpinspect-shadow-pause-time mx continue)) + (ignore-errors (thread-yield)))) (defun phpi-shadow-make-queue-subscription (shadow) (lambda () (setf (phpi-shadow-synced-p shadow) nil) + (setf (phpi-shadow-index-synced-p shadow) nil) (phpi-shadow-wakeup-thread shadow))) (defun phpi-shadow--thread-make-parser-interrupt-predicate () @@ -790,8 +829,6 @@ If provided, PROJECT must be an instance of `phpinspect-project'." (with-current-buffer (phpi-shadow-buffer shadow) (phpi-change-apply change (current-buffer)) - ;;(message "parsing") - (let* ((buffer (phpi-shadow-origin shadow)) (pctx (phpinspect-make-pctx :incremental t @@ -808,13 +845,11 @@ If provided, PROJECT must be an instance of `phpinspect-project'." (setf (phpinspect-buffer-tree buffer) result)) - ;;(message "setting map") (phpinspect-buffer--set-map buffer (phpinspect-pctx-bmap pctx) (phpinspect-pctx-previous-bmap pctx))))) (defun phpi-shadow-parse-fresh (shadow) (with-current-buffer (phpi-shadow-buffer shadow) - ;;(message "parsing fresh") (let* ((buffer (phpi-shadow-origin shadow)) (pctx (phpinspect-make-pctx :incremental t @@ -829,8 +864,6 @@ If provided, PROJECT must be an instance of `phpinspect-project'." (setf (phpinspect-buffer-tree buffer) result) - ;;(message "Result: %s" result) - (phpinspect-buffer--set-map buffer (phpinspect-pctx-bmap pctx) nil))))) @@ -840,47 +873,126 @@ If provided, PROJECT must be an instance of `phpinspect-project'." (pop-to-buffer (phpi-shadow-buffer (phpinspect-buffer-shadow buffer)))) (defun phpi-shadow-make-thread-function (shadow) - ;;(message "making thread function") (lambda () (let ((inhibit-quit t)) - ;;(message "Func starting") (while t - ;;(message "shadow thread working") - (if-let ((task (phpinspect-queue-dequeue (phpi-shadow-queue shadow)))) - (progn - (pcase task - ((pred phpinspect-change-p) - (phpi-shadow-process-change shadow task)) - ('parse-fresh - (phpi-shadow-parse-fresh shadow)) - (_ - (phpinspect-message - "Shadow thread received unsupported task type: %s" - (type-of task)))) - - ;; Rest after task completion - (phpi-shadow-thread-check-pause)) - - ;; No work to do, join main thread - (setf (phpi-shadow-synced-p shadow) t) - (ignore-error 'phpinspect-wakeup-shadow - (thread-join main-thread))))))) + (condition-case err + (ignore-error phpinspect-wakeup-shadow + (if-let ((task (phpinspect-queue-dequeue (phpi-shadow-queue shadow)))) + (progn + (pcase task + ((pred phpinspect-change-p) + (phpi-shadow-process-change shadow task)) + ('parse-fresh + (phpi-shadow-parse-fresh shadow)) + (_ + (phpinspect-message + "Shadow thread received unsupported task type: %s" + (type-of task)))) + + ;; Rest after task completion + (phpi-shadow-thread-check-pause)) + + ;; No parsing work to do, we're in sync with the origin buffer + (setf (phpi-shadow-synced-p shadow) t) + + (if (phpi-shadow-index-synced-p shadow) + ;; Index has been synced, we can join the main thread and + ;; wait for more tasks. + (ignore-errors + (thread-join main-thread)) + ;; Update project index + (phpi-shadow-update-project-index shadow) + + ;; Only flag index as synced when no additional tasks have been + ;; enqueued while indexing. + (unless (phpinspect-queue-first (phpi-shadow-queue shadow)) + (setf (phpi-shadow-index-synced-p shadow) t))))) + (error + (phpinspect-message "%s encountered unexpected error: %s" + (thread-name (current-thread)) + err))))))) + ;; (error + ;; (phpinspect-message "%s exited: %s" + ;; (thread-name (current-thread)) + ;; err)))))) (defun phpi-shadow-thread-live-p (shadow) (thread-live-p (phpi-shadow-thread shadow))) -(defun phpi-shadow-await-synced (shadow &optional allow-interrupt) - (cl-assert (phpi-shadow-thread-live-p shadow)) +(defun phpi-shadow-assert-live-p (shadow) + (unless (phpi-shadow-thread-live-p shadow) + (error "Shadow thread exited: %s" + (thread-last-error (phpi-shadow-thread shadow))))) - (while (not (phpi-shadow-synced-p shadow)) - (when (and allow-interrupt (phpinspect--input-pending-p)) - (throw 'phpinspect-interrupted nil)) +(defun phpi-shadow-is-me-p (shadow) + (eq (current-thread) (phpi-shadow-thread shadow))) - (unless (phpi-shadow-thread-live-p shadow) - (error "Shadow thread exited: %s" - (thread-last-error (phpi-shadow-thread shadow)))) +(defun phpi-shadow-await-predicate (shadow predicate allow-interrupt) + (phpi-shadow-assert-live-p shadow) + + (unless (phpi-shadow-is-me-p shadow) + + (while (not (or (funcall predicate shadow) quit-flag)) + (when (and (phpinspect--input-pending-p t) allow-interrupt) + (throw 'phpinspect-interrupted nil)) - (sleep-for 0.005))) + (phpi-shadow-assert-live-p shadow) + + (phpi-shadow-wakeup-thread shadow) + (thread-yield)))) + +(defun phpi-shadow-await-synced (shadow &optional allow-interrupt) + (phpi-shadow-await-predicate shadow #'phpi-shadow-synced-p allow-interrupt)) + +(defun phpi-shadow-await-index-synced (shadow &optional allow-interrupt) + (phpi-shadow-await-predicate shadow #'phpi-shadow-index-synced-p allow-interrupt)) + +(defun phpi-shadow-process-deletions (shadow) + (let ((buffer (phpi-shadow-origin shadow))) + ;; Process deleted tokens + (dolist (deletion (phpinspect-buffer--deletions buffer)) + (pcase (phpinspect-meta-token deletion) + ((pred phpinspect--can-delete-buffer-index-for-token) + (phpinspect-buffer-delete-index-for-token buffer (phpinspect-meta-token deletion))) + ((pred phpinspect-use-trait-p) + (when-let ((class (seq-find (phpinspect-meta-token-predicate #'phpinspect-class-p) + (phpinspect-buffer-tokens-enclosing-point + buffer (phpinspect-meta-start deletion)))) + (declaration (phpinspect-meta-find-first-child-matching-token + class #'phpinspect-class-declaration-p))) + (phpinspect-buffer--index-class-declaration buffer declaration 'force))))) + + (setf (phpinspect-buffer--deletions buffer) nil))) + +(defun phpi-shadow-process-additions (shadow) + ;; Process newly parsed tokens + (let ((buffer (phpi-shadow-origin shadow))) + (dolist (addition (phpinspect-buffer--additions buffer)) + (pcase (phpinspect-meta-token addition) + ((pred phpinspect-class-declaration-p) + (phpinspect-buffer--index-class-declaration buffer addition)) + ((pred phpinspect-function-p) + (phpinspect-buffer--index-function buffer addition)) + ((pred phpinspect-use-trait-p) + (phpinspect-buffer--index-trait-use buffer addition)) + ((pred phpinspect-this-p) + (phpinspect-buffer--index-this buffer addition)) + ((or (pred phpinspect-class-variable-p) + (pred phpinspect-const-p)) + (phpinspect-buffer--index-class-variable buffer addition))) + + ;; Pause in between potentially expensive indexing operations. + (phpi-shadow-thread-check-pause)) + + (setf (phpinspect-buffer--additions buffer) nil))) + +(defun phpi-shadow-update-project-index (shadow) + (when (phpinspect-buffer-project (phpi-shadow-origin shadow)) + (phpinspect--log "Updating project index") + (phpi-shadow-process-deletions shadow) + (phpi-shadow-thread-check-pause) + (phpi-shadow-process-additions shadow))) (defun phpi-shadow-make-thread (shadow) (make-thread diff --git a/phpinspect-completion.el b/phpinspect-completion.el index 08f633edf8..5bdeedbf6a 100644 --- a/phpinspect-completion.el +++ b/phpinspect-completion.el @@ -42,7 +42,7 @@ (target nil :documentation "The object that this completion is aimed at/completing towards") - (value nil :type string) + (name nil :type string) (meta nil :type string) (annotation nil :type string) (kind nil :type symbol)) @@ -58,8 +58,8 @@ candidate. Candidates can be indexed functions and variables.") :type integer) (completion-end nil :type integer) - (completions (obarray-make) - :type obarray + (completions (make-hash-table :test #'equal :size 10000 :rehash-size 2) + :type hash-table :documentation "A list of completion strings") (has-candidates nil)) @@ -70,29 +70,23 @@ candidate. Candidates can be indexed functions and variables.") (cl-defmethod phpinspect--completion-list-add ((comp-list phpinspect--completion-list) (completion phpinspect--completion)) - ;; Ignore completions in an invalid state (nil values) - (when (phpinspect--completion-value completion) + ;; Ignore completions in an invalid state (nil names) + (when (phpinspect--completion-name completion) (setf (phpinspect--completion-list-has-candidates comp-list) t) - (unless (intern-soft (phpinspect--completion-value completion) - (phpinspect--completion-list-completions comp-list)) - (set (intern (phpinspect--completion-value completion) - (phpinspect--completion-list-completions comp-list)) - completion)))) + (unless (gethash + (phpinspect--completion-name completion) + (phpinspect--completion-list-completions comp-list)) + (puthash (phpinspect--completion-name completion) + completion + (phpinspect--completion-list-completions comp-list))))) (cl-defmethod phpinspect--completion-list-get-metadata ((comp-list phpinspect--completion-list) (completion-name string)) - (let ((comp-sym (intern-soft completion-name - (phpinspect--completion-list-completions comp-list)))) - (when comp-sym - (symbol-value comp-sym)))) - + (gethash completion-name (phpinspect--completion-list-completions comp-list))) (cl-defmethod phpinspect--completion-list-strings ((comp-list phpinspect--completion-list)) - (let (strings) - (obarray-map (lambda (sym) (push (symbol-name sym) strings)) - (phpinspect--completion-list-completions comp-list)) - strings)) + (hash-table-keys (phpinspect--completion-list-completions comp-list))) (cl-defstruct (phpinspect-completion-query (:constructor phpinspect-make-completion-query)) (completion-point 0 @@ -406,7 +400,7 @@ Returns list of `phpinspect--completion'." ;; on less powerful machines, consider implementing an LRU. (defun phpinspect-make-fn-completion (completion-candidate) (phpinspect--construct-completion - :value (phpi-fn-name completion-candidate) + :name (phpi-fn-name completion-candidate) :meta (concat "(" (mapconcat (lambda (arg) (concat "$" (if (> (length (car arg)) 8) (truncate-string-to-width (car arg) 8 nil) @@ -437,7 +431,7 @@ Returns list of `phpinspect--completion'." (cl-defmethod phpinspect--make-completion ((type phpinspect--type)) (phpinspect--construct-completion - :value (propertize (phpinspect--type-base-name type) 'face 'font-lock-type-face) + :name (propertize (phpinspect--type-base-name type) 'face 'font-lock-type-face) :meta (phpinspect--format-type-name type) :target type :kind 'class)) @@ -445,7 +439,7 @@ Returns list of `phpinspect--completion'." (cl-defmethod phpinspect--make-completion ((completion-candidate phpinspect--variable)) (phpinspect--construct-completion - :value (phpinspect--variable-name completion-candidate) + :name (phpinspect--variable-name completion-candidate) :meta (phpinspect--display-format-type-name (or (phpinspect--variable-type completion-candidate) phpinspect--null-type)) @@ -458,7 +452,7 @@ Returns list of `phpinspect--completion'." (cl-defmethod phpinspect--make-completion ((keyword phpinspect-suggest-keyword)) (phpinspect--construct-completion - :value (propertize (phpinspect-suggest-keyword-word keyword) 'face 'font-lock-keyword-face) + :name (propertize (phpinspect-suggest-keyword-word keyword) 'face 'font-lock-keyword-face) :target keyword :kind 'keyword :meta "keyword")) @@ -473,14 +467,13 @@ Returns list of `phpinspect--completion'." (defun phpinspect-complete-at-point () (catch 'phpinspect-interrupted - (let ((comp-list (phpinspect-completion-query-execute (phpinspect--get-completion-query))) - strings) - (phpinspect--log "Completion list: %s" comp-list) - (obarray-map (lambda (sym) (push (symbol-name sym) strings)) (phpinspect--completion-list-completions comp-list)) + (let ((comp-list (phpinspect-completion-query-execute (phpinspect--get-completion-query)))) + (and (phpinspect--completion-list-has-candidates comp-list) (list (phpinspect--completion-list-completion-start comp-list) (phpinspect--completion-list-completion-end comp-list) - strings + (completion-table-dynamic (lambda (_) + (phpinspect--completion-list-strings comp-list))) :affixation-function (lambda (completions) (let (affixated completion) diff --git a/phpinspect-diagnose.el b/phpinspect-diagnose.el index b49f80eb9a..0b5b7f1de9 100644 --- a/phpinspect-diagnose.el +++ b/phpinspect-diagnose.el @@ -65,7 +65,6 @@ (when meta (let ((stack (list meta))) (while-let ((current (pop stack))) - (message "checking") ;; A token cannot be a parent of itself. (when (eq (phpinspect-meta-parent current) current) (let ((message diff --git a/phpinspect-parser.el b/phpinspect-parser.el index cfdcdae59b..def9f99fe4 100644 --- a/phpinspect-parser.el +++ b/phpinspect-parser.el @@ -154,6 +154,7 @@ text at point and returns the resulting token." ;; (setq token-meta (phpinspect-meta-find-child-starting-at token-meta original-point))) (if (or (not token-meta) + (not (phpinspect--token-recyclable-p (phpinspect-meta-token token-meta))) (phpinspect-root-p (phpinspect-meta-token token-meta)) (phpinspect-incomplete-token-p (phpinspect-meta-token token-meta)) (phpi-change-tainted-token-p change token-meta)) @@ -281,6 +282,7 @@ is able to reuse an already parsed tree." (when (looking-at (phpinspect-handler-regexp whitespace)) (,(phpinspect-handler-func-name 'whitespace) (match-string 0))) + (while (and (< (point) max-point) (if continue-condition (funcall continue-condition) t) (not ,(if delimiter-predicate @@ -383,6 +385,9 @@ is able to reuse an already parsed tree." :read-only t :documentation "Name of the keyword that is used as car of the root token, in string form without \":\" prefix.") + (recycle-only-delimited + nil + :type boolean) (handlers '(array tag equals list comma attribute-reference static-attribute-reference variable assignment-operator whitespace scope-keyword @@ -437,6 +442,7 @@ version of the parser function." (phpinspect-parser-delimiter-predicate parser) (phpinspect-parser-delimiter-condition parser))))) + (cl-defmethod phpinspect-parser-compile-entry ((parser phpinspect-parser)) (let ((func-name (phpinspect-parser-func-name (phpinspect-parser-name parser))) (incremental-name (phpinspect-parser-func-name (phpinspect-parser-name parser) "incremental")) @@ -499,14 +505,49 @@ parsing incrementally." (defun phpinspect-handler-bound-p (symbol) (get symbol 'phpinspect--handler)) +(eval-and-compile + (defun phpinspect-make-recycle-predicate (predicate-name names) + (let (cases + (token-sym (gensym))) + (push `(t t) cases) + (dolist (name names) + (catch 'phpinspect--continue + (let ((parser (symbol-value name)) + body) + (unless (or (and (phpinspect-parser-delimiter-condition parser) + (phpinspect-parser-delimiter-predicate parser)) + (and (phpinspect-parser-delimiter-predicate parser) + (phpinspect-parser-recycle-only-delimited parser))) + (throw 'phpinspect--continue nil)) + + (setq body `(,(phpinspect-parser-delimiter-predicate parser) (car (last ,token-sym)))) + + (when-let ((condition (phpinspect-parser-delimiter-condition parser))) + (setq body `(if (,(phpinspect-parser-delimiter-condition parser) ,token-sym) + ,body + ,(not (phpinspect-parser-recycle-only-delimited parser))))) + + (push `((eq (car ,token-sym) ,(intern (concat ":" (phpinspect-parser-tree-keyword parser)))) + ,body) + cases)))) + + `(defun ,predicate-name (,token-sym) + (cond ,@cases))))) + +(eval-and-compile + (defun phpinspect--get-parser-function-syms () + (let (names incremental-names) + (obarray-map (lambda (sym) + (cond ((phpinspect-parser-func-bound-p sym) + (push sym names)) + ((phpinspect-incremental-parser-func-bound-p sym) + (push sym incremental-names)))) + obarray) + (list names incremental-names)))) + (defmacro phpinspect-define-parser-functions () - (let (names incremental-names function-definitions) - (obarray-map (lambda (sym) - (cond ((phpinspect-parser-func-bound-p sym) - (push sym names)) - ((phpinspect-incremental-parser-func-bound-p sym) - (push sym incremental-names)))) - obarray) + (pcase-let ((`(,names ,incremental-names) (phpinspect--get-parser-function-syms)) + (function-definitions)) (dolist (name names) (push (phpinspect-parser-compile-entry (symbol-value name)) @@ -522,7 +563,10 @@ parsing incrementally." (push 'progn function-definitions) - function-definitions)) + `(progn + ,function-definitions + ,(phpinspect-make-recycle-predicate + 'phpinspect--token-recyclable-p names)))) (phpinspect-defhandler comma (comma &rest _ignored) "Handler for comma tokens" @@ -739,11 +783,13 @@ nature like argument lists" (phpinspect-defparser use :tree-keyword "use" + :recycle-only-delimited t :handlers '(comment word tag block-without-scopes comma terminator) :delimiter-predicate #'phpinspect-end-of-use-p) (phpinspect-defparser use-trait :tree-keyword "use-trait" + :recycle-only-delimited t :handlers '(comment word tag block-without-scopes comma terminator) :delimiter-predicate #'phpinspect-end-of-use-p) @@ -803,6 +849,7 @@ To parse trait use statements in class bodies, see (phpinspect-defparser namespace :tree-keyword "namespace" + :recycle-only-delimited t :delimiter-condition #'phpinspect--namespace-should-end-at-block-p :delimiter-predicate #'phpinspect-block-p) @@ -824,6 +871,7 @@ To parse trait use statements in class bodies, see (phpinspect-defparser const :tree-keyword "const" + :recycle-only-delimited t :handlers '(word comment assignment-operator string string-concatenator array terminator) :delimiter-predicate #'phpinspect-end-of-token-p) @@ -963,6 +1011,7 @@ Returns the consumed text string without face properties." (phpinspect-defparser declaration :tree-keyword "declaration" :handlers '(comment word list terminator tag) + :recycle-only-delimited t :delimiter-predicate #'phpinspect-end-of-token-p) (phpinspect-defhandler function-keyword (start-token max-point) @@ -987,6 +1036,7 @@ Returns the consumed text string without face properties." :handlers '(function-keyword static-keyword const-keyword class-variable here-doc string string-concatenator terminator tag comment assignment-operator array word) + :recycle-only-delimited t :delimiter-predicate #'phpinspect--scope-terminator-p) (phpinspect-defparser scope-private @@ -994,10 +1044,12 @@ Returns the consumed text string without face properties." :handlers '(function-keyword static-keyword const-keyword class-variable here-doc string string-concatenator terminator tag comment assignment-operator array word) + :recycle-only-delimited t :delimiter-predicate #'phpinspect--scope-terminator-p) (phpinspect-defparser scope-protected :tree-keyword "protected" + :recycle-only-delimited t :handlers '(function-keyword static-keyword const-keyword class-variable here-doc string string-concatenator terminator tag comment assignment-operator array word) @@ -1017,6 +1069,7 @@ Returns the consumed text string without face properties." (phpinspect-defparser static :tree-keyword "static" + :recycle-only-delimited t :handlers '(comment function-keyword class-variable array word terminator tag) :delimiter-predicate #'phpinspect--static-terminator-p) @@ -1069,6 +1122,17 @@ Returns the consumed text string without face properties." (lambda () (not (char-equal (char-after) ?{))) 'root))) +(phpinspect-defhandler type-declaration (_start-token max-point) + "Handler for class, interface, trait declarations." + ((regexp . (phpinspect--word-handler-regexp)) + (inline . t)) + (inline-quote + (phpinspect--parse-class-declaration + (current-buffer) + ,max-point + nil + (lambda () (not (char-equal (char-after) ?{)))))) + (define-inline phpinspect--skip-over-word (word-plus-whitespace) (inline-quote (forward-char (length (phpinspect--strip-word-end-space ,word-plus-whitespace))))) diff --git a/phpinspect-pipeline.el b/phpinspect-pipeline.el index 2579d1f83f..4ffd54fec6 100644 --- a/phpinspect-pipeline.el +++ b/phpinspect-pipeline.el @@ -134,7 +134,7 @@ directories." (let ((mx (make-mutex))) (phpinspect-thread-pause phpinspect-pipeline-pause-time mx (make-condition-variable mx "phpinspect-pipeline-pause"))) - (thread-yield)))) + (ignore-errors (thread-yield))))) (define-inline phpinspect--read-pipeline-emission (&rest body) (push 'progn body) diff --git a/phpinspect-token-predicates.el b/phpinspect-token-predicates.el index b21cbfeeef..339d476e2f 100644 --- a/phpinspect-token-predicates.el +++ b/phpinspect-token-predicates.el @@ -123,14 +123,15 @@ Type can be any of the token types returned by (inline-letevals (token) (inline-quote (and (phpinspect-class-p ,token) - (phpinspect-incomplete-block-p (car (last ,token))))))) + (or (not (phpinspect-block-p (car (last ,token)))) + (phpinspect-incomplete-block-p (car (last ,token)))))))) (define-inline phpinspect-incomplete-namespace-p (token) (inline-letevals (token) (inline-quote (and (phpinspect-namespace-p ,token) (or (phpinspect-incomplete-block-p (car (last ,token))) - (phpinspect-incomplete-class-p (car (last ,token)))))))) + (phpinspect-incomplete-class-p (car (last ,token)))))))) (define-inline phpinspect-function-p (token) (inline-quote (phpinspect-token-type-p ,token :function))) @@ -165,6 +166,10 @@ Type can be any of the token types returned by (inline-quote (phpinspect-token-type-p ,token :declaration :class-declaration))) +(define-inline phpinspect-function-declaration-p (token) + (inline-quote + (phpinspect-token-type-p ,token :declaration))) + (define-inline phpinspect-class-declaration-p (token) (inline-quote (phpinspect-token-type-p ,token :class-declaration))) diff --git a/phpinspect-util.el b/phpinspect-util.el index aa6d03c154..3d50a7866c 100644 --- a/phpinspect-util.el +++ b/phpinspect-util.el @@ -271,8 +271,8 @@ context for completion." ,@body)) (defun phpinspect--input-pending-p (&optional check-timers) - (unless noninteractive - (input-pending-p check-timers))) + (and (input-pending-p check-timers) + (not noninteractive))) (defun phpinspect-thread-pause (pause-time mx continue) "Pause current thread using MX and CONTINUE for PAUSE-TIME idle seconds. @@ -285,7 +285,7 @@ CONTINUE must be a condition-variable" pause-time nil (lambda () (with-mutex mx (condition-notify continue)))) - (with-mutex mx (condition-wait continue)) + (ignore-errors (with-mutex mx (condition-wait continue))) (phpinspect--log "Thread '%s' continuing execution" (thread-name (current-thread)))) (provide 'phpinspect-util) diff --git a/test/test-buffer.el b/test/test-buffer.el index 7de760b468..26724f87d3 100644 --- a/test/test-buffer.el +++ b/test/test-buffer.el @@ -638,7 +638,6 @@ class Bar { (let ((class (phpinspect-project-get-typedef project (phpinspect--make-type :name "\\Foo\\Bar")))) - (should class) (should (= 1 (length (phpi-typedef-get-methods class)))) @@ -648,7 +647,6 @@ class Bar { (phpinspect-buffer-parse buffer 'no-interrupt) (phpinspect-buffer-update-project-index buffer) - (should (= 2 (length (phpi-typedef-get-methods class)))) (goto-char 36) @@ -743,32 +741,46 @@ class TestClass (should (equal '(:root (:comment)) (phpinspect-buffer-parse buffer)))))) -(ert-deftest phpinspect-buffer-deletion-registration () - "Check if buffer accuratly registers token deletions/alterations." +;; (ert-deftest phpinspect-buffer-deletion-registration () +;; "Check if buffer accuratly registers token deletions/alterations." - (with-temp-buffer - (let ((buffer (phpinspect-claim-buffer (current-buffer) (phpinspect--make-dummy-project)))) +;; (with-temp-buffer +;; (let ((buffer (phpinspect-claim-buffer (current-buffer) (phpinspect--make-dummy-project)))) + +;; (insert "<?php aaa bbb ccc") + +;; (should (equal '(:root (:word "aaa") (:word "bbb") (:word "ccc")) (phpinspect-buffer-parse buffer))) +;; (should-not (phpinspect-buffer--deletions buffer)) - (insert "<?php aaa bbb ccc") +;; (should (length= (phpinspect-buffer--additions buffer) 4)) - (should (equal '(:root (:word "aaa") (:word "bbb") (:word "ccc")) (phpinspect-buffer-parse buffer))) - (should-not (phpinspect-buffer--deletions buffer)) +;; (goto-char (- (point-max) 5)) +;; (insert "a") +;; (should (equal '(:root (:word "aaa") (:word "bbab") (:word "ccc")) (phpinspect-buffer-parse buffer))) - (should (length= (phpinspect-buffer--additions buffer) 4)) +;; (should (length= (phpinspect-buffer--deletions buffer) 2)) +;; (should (length= (phpinspect-buffer--additions buffer) 6)) - (goto-char (- (point-max) 5)) - (insert "a") - (should (equal '(:root (:word "aaa") (:word "bbab") (:word "ccc")) (phpinspect-buffer-parse buffer))) +;; (goto-char 6) +;; (insert "{") +;; (should (equal '(:root (:incomplete-block (:word "aaa") (:word "bbab") (:word "ccc"))) +;; (phpinspect-buffer-parse buffer))) +;; (should (length= (phpinspect-buffer--deletions buffer) 3)) +;; (should (length= (phpinspect-buffer--additions buffer) 8)) - (should (length= (phpinspect-buffer--deletions buffer) 2)) - (should (length= (phpinspect-buffer--additions buffer) 6)) +;; (should (seq-every-p #'phpinspect-meta-p (phpinspect-buffer--additions buffer))) +;; (should (seq-every-p #'phpinspect-meta-p (phpinspect-buffer--deletions buffer)))))) - (goto-char 6) - (insert "{") - (should (equal '(:root (:incomplete-block (:word "aaa") (:word "bbab") (:word "ccc"))) - (phpinspect-buffer-parse buffer))) - (should (length= (phpinspect-buffer--deletions buffer) 3)) - (should (length= (phpinspect-buffer--additions buffer) 8)) +(ert-deftest phpinspect-buffer-parse-class-insertion () + (with-temp-buffer + (let ((buffer (phpinspect-claim-buffer (current-buffer)))) + (insert "<?php ") + + (insert "class") + (phpinspect-buffer-parse buffer) + (insert " Name") + (phpinspect-buffer-parse buffer) + (insert " {} ") - (should (seq-every-p #'phpinspect-meta-p (phpinspect-buffer--additions buffer))) - (should (seq-every-p #'phpinspect-meta-p (phpinspect-buffer--deletions buffer)))))) + (should (equal '(:root (:class (:class-declaration (:word "Name")) (:block))) + (phpinspect-buffer-parse buffer)))))) diff --git a/test/test-parser.el b/test/test-parser.el index 224aa485ba..b2a87855fb 100644 --- a/test/test-parser.el +++ b/test/test-parser.el @@ -273,3 +273,8 @@ interface Test (dolist (code (list "//" "/*" "// " "/* ")) (should (phpinspect-parse-string code)))) + +(ert-deftest phpinspect-parser-token-recycle-predicate () + (should (phpinspect--token-recyclable-p '(:list))) + (should-not (phpinspect--token-recyclable-p '(:namespace))) + (should (phpinspect--token-recyclable-p '(:namespace (:word "bla") (:block)))))