Function: syntax-ppss

syntax-ppss is a byte-compiled function defined in syntax.el.gz.

Signature

(syntax-ppss &optional POS)

Documentation

Parse-Partial-Sexp State at POS, defaulting to point.

If POS is given, this function moves point to POS.

The returned value is the same as that of parse-partial-sexp run from point-min to POS except that values at positions 2 and 6 in the returned list (counting from 0) cannot be relied upon.

It is necessary to call syntax-ppss-flush-cache explicitly if this function is called while before-change-functions is temporarily let-bound, or if the buffer is modified without running the hook.

View in manual

Probably introduced at or before Emacs version 22.1.

Source Code

;; Defined in /usr/src/emacs/lisp/emacs-lisp/syntax.el.gz
(defun syntax-ppss (&optional pos)
  "Parse-Partial-Sexp State at POS, defaulting to point.
If POS is given, this function moves point to POS.

The returned value is the same as that of `parse-partial-sexp'
run from `point-min' to POS except that values at positions 2 and 6
in the returned list (counting from 0) cannot be relied upon.

It is necessary to call `syntax-ppss-flush-cache' explicitly if
this function is called while `before-change-functions' is
temporarily let-bound, or if the buffer is modified without
running the hook."
  ;; Default values.
  (unless pos (setq pos (point)))
  (syntax-propertize pos)
  ;;
  (with-syntax-table (or syntax-ppss-table (syntax-table))
    (let* ((cell (syntax-ppss--data))
           (ppss-last (car cell))
           (ppss-cache (cdr cell))
           (old-ppss (cdr ppss-last))
           (old-pos (car ppss-last))
           (ppss nil)
           (pt-min (point-min)))
      (if (and old-pos (> old-pos pos)) (setq old-pos nil))
      ;; Use the OLD-POS if usable and close.  Don't update the `last' cache.
      (condition-case nil
          (if (and old-pos (< (- pos old-pos)
                              ;; The time to use syntax-begin-function and
                              ;; find PPSS is assumed to be about 2 * distance.
                              (let ((pair (aref syntax-ppss-stats 5)))
                                (/ (* 2 (cdr pair)) (car pair)))))
              (progn
                (syntax-ppss--update-stats 0 old-pos pos)
                (parse-partial-sexp old-pos pos nil nil old-ppss))

            (cond
             ;; Use OLD-PPSS if possible and close enough.
             ((and (not old-pos) old-ppss
                   ;; If `pt-min' is too far from `pos', we could try to use
                   ;; other positions in (nth 9 old-ppss), but that doesn't
                   ;; seem to happen in practice and it would complicate this
                   ;; code (and the before-change-function code even more).
                   ;; But maybe it would be useful in "degenerate" cases such
                   ;; as when the whole file is wrapped in a set
                   ;; of parentheses.
                   (setq pt-min (or (syntax-ppss-toplevel-pos old-ppss)
                                    (nth 2 old-ppss)))
                   (<= pt-min pos) (< (- pos pt-min) syntax-ppss-max-span))
              (syntax-ppss--update-stats 1 pt-min pos)
              (setq ppss (parse-partial-sexp pt-min pos)))
             ;; The OLD-* data can't be used.  Consult the cache.
             (t
              (let ((cache-pred nil)
                    (cache ppss-cache)
                    (pt-min (point-min))
                    ;; I differentiate between PT-MIN and PT-BEST because
                    ;; I feel like it might be important to ensure that the
                    ;; cache is only filled with 100% sure data (whereas
                    ;; syntax-begin-function might return incorrect data).
                    ;; Maybe that's just stupid.
                    (pt-best (point-min))
                    (ppss-best nil))
                ;; look for a usable cache entry.
                (while (and cache (< pos (caar cache)))
                  (setq cache-pred cache)
                  (setq cache (cdr cache)))
                (if cache (setq pt-min (caar cache) ppss (cdar cache)))

                ;; Setup the before-change function if necessary.
                (unless (or ppss-cache ppss-last)
                  ;; Note: combine-change-calls-1 needs to be kept in sync
                  ;; with this!
                  (add-hook 'before-change-functions
                            #'syntax-ppss-flush-cache
                            ;; We should be either the very last function on
                            ;; before-change-functions or the very first on
                            ;; after-change-functions.
                            99 t))

                ;; Use the best of OLD-POS and CACHE.
                (if (or (not old-pos) (< old-pos pt-min))
                    (setq pt-best pt-min ppss-best ppss)
                  (syntax-ppss--update-stats 4 old-pos pos)
                  (setq pt-best old-pos ppss-best old-ppss))

                ;; Use the `syntax-begin-function' if available.
                ;; We could try using that function earlier, but:
                ;; - The result might not be 100% reliable, so it's better to use
                ;;   the cache if available.
                ;; - The function might be slow.
                ;; - If this function almost always finds a safe nearby spot,
                ;;   the cache won't be populated, so consulting it is cheap.
                (when (and syntax-begin-function
                           (progn (goto-char pos)
                                  (funcall syntax-begin-function)
                                  ;; Make sure it's better.
                                  (> (point) pt-best))
                           ;; Simple sanity checks.
                           (< (point) pos) ; backward-paragraph can fail here.
                           (not (memq (get-text-property (point) 'face)
                                      '(font-lock-string-face font-lock-doc-face
                                                              font-lock-comment-face))))
                  (syntax-ppss--update-stats 5 (point) pos)
                  (setq pt-best (point) ppss-best nil))

                (cond
                 ;; Quick case when we found a nearby pos.
                 ((< (- pos pt-best) syntax-ppss-max-span)
                  (syntax-ppss--update-stats 2 pt-best pos)
                  (setq ppss (parse-partial-sexp pt-best pos nil nil ppss-best)))
                 ;; Slow case: compute the state from some known position and
                 ;; populate the cache so we won't need to do it again soon.
                 (t
                  (syntax-ppss--update-stats 3 pt-min pos)
                  (setq syntax-ppss--updated-cache t)

                  ;; If `pt-min' is too far, add a few intermediate entries.
                  (while (> (- pos pt-min) (* 2 syntax-ppss-max-span))
                    (setq ppss (parse-partial-sexp
                                pt-min (setq pt-min (/ (+ pt-min pos) 2))
                                nil nil ppss))
                    (push (cons pt-min ppss)
                          (if cache-pred (cdr cache-pred) ppss-cache)))

                  ;; Compute the actual return value.
                  (setq ppss (parse-partial-sexp pt-min pos nil nil ppss))

                  ;; Debugging check.
                  ;; (let ((real-ppss (parse-partial-sexp (point-min) pos)))
                  ;;   (setcar (last ppss 4) 0)
                  ;;   (setcar (last real-ppss 4) 0)
                  ;;   (setcar (last ppss 8) nil)
                  ;;   (setcar (last real-ppss 8) nil)
                  ;;   (unless (equal ppss real-ppss)
                  ;;     (message "!!Syntax: %s != %s" ppss real-ppss)
                  ;;     (setq ppss real-ppss)))

                  ;; Store it in the cache.
                  (let ((pair (cons pos ppss)))
                    (if cache-pred
                        (if (> (- (caar cache-pred) pos) syntax-ppss-max-span)
                            (push pair (cdr cache-pred))
                          (setcar cache-pred pair))
                      (if (or (null ppss-cache)
                              (> (- (caar ppss-cache) pos)
                                 syntax-ppss-max-span))
                          (push pair ppss-cache)
                        (setcar ppss-cache pair)))))))))

            (setq syntax-ppss--updated-cache t)
            (setq ppss-last (cons pos ppss))
            (setcar cell ppss-last)
            (setcdr cell ppss-cache)
            ppss)
        (args-out-of-range
         ;; If the buffer is more narrowed than when we built the cache,
         ;; we may end up calling parse-partial-sexp with a position before
         ;; point-min.  In that case, just parse from point-min assuming
         ;; a nil state.
         (parse-partial-sexp (point-min) pos))))))