+ (if scores
+ ;; cand may be in scores and still have no global score left
+ (+ (or (cdr (assoc nil scores)) 0)
+ (or (cdr (assoc major-mode scores)) 0))
+ 0)))
+
+(defvar company-statistics--context nil
+ "Current completion context, a list of entries searched using `assoc'.")
+
+(defun company-statistics--last-keyword ()
+ "Return last keyword, ie, text of region fontified with the
+font-lock-keyword-face up to point, or nil."
+ (let ((face-pos (point)))
+ (while (and (number-or-marker-p face-pos)
+ (< (point-min) face-pos)
+ (not (eq (get-text-property (1- face-pos) 'face)
+ 'font-lock-keyword-face)))
+ (setq face-pos
+ (previous-single-property-change face-pos 'face nil (point-min))))
+ (when (and (number-or-marker-p face-pos)
+ (eq (get-text-property (max (point-min) (1- face-pos)) 'face)
+ 'font-lock-keyword-face))
+ (list :keyword
+ (buffer-substring-no-properties
+ (previous-single-property-change face-pos 'face nil (point-min))
+ face-pos)))))
+
+(defun company-statistics--parent-symbol ()
+ "Return symbol immediately preceding current completion prefix, or nil.
+May be separated by punctuation, but not by whitespace."
+ ;; expects to be at start of company-prefix; little sense for lisps
+ (let ((preceding (save-excursion
+ (unless (zerop (skip-syntax-backward "."))
+ (substring-no-properties (symbol-name (symbol-at-point)))))))
+ (when preceding
+ (list :symbol preceding))))
+
+(defun company-statistics--file-name ()
+ "Return buffer file name, or nil."
+ (when buffer-file-name
+ (list :file buffer-file-name)))
+
+(defun company-statistics-capture-context-heavy (manual)
+ "Calculate some context, once for the whole completion run."
+ (save-excursion
+ (backward-char (length company-prefix))
+ (setq company-statistics--context
+ (delq nil
+ (list (company-statistics--last-keyword)
+ (company-statistics--parent-symbol)
+ (company-statistics--file-name))))))
+
+(defun company-statistics-score-change-heavy (cand)
+ "Count for global score, mode context, last keyword, parent symbol,
+buffer file name."
+ (let ((last-kwd (assoc :keyword company-statistics--context))
+ (parent-symbol (assoc :symbol company-statistics--context))
+ (file (assoc :file company-statistics--context)))
+ (nconc ;when's nil is removed
+ (list (cons nil 1)
+ (cons major-mode 1)) ;major-mode is never nil
+ ;; only add pieces of context if non-nil
+ (when last-kwd (list (cons last-kwd 1)))
+ (when parent-symbol (list (cons parent-symbol 1)))
+ (when file (list (cons file 1))))))
+
+(defun company-statistics-score-calc-heavy (cand)
+ "Global score, and bonus for matching major mode, last keyword, parent
+symbol, buffer file name."
+ (let ((scores (gethash cand company-statistics--scores))
+ (last-kwd (assoc :keyword company-statistics--context))
+ (parent-symbol (assoc :symbol company-statistics--context))
+ (file (assoc :file company-statistics--context)))