          (mode-hook-sym (intern (format "%s-hook" mode-name)))
          (mode-init-sym (intern (format "narf--init-project-%s" mode-name))))
     (let ((modes (plist-get body :modes))
+          (pred  (plist-get body :when))
           (match (plist-get body :match))
           (files (plist-get body :files))
           (build (plist-get body :build))

  "Associate a major or minor mode to certain patterns and project files."
  (declare (indent 1))
  (let ((minor (plist-get rest :minor))
        (in (plist-get rest :in))
        (match (plist-get rest :match))
        (files (plist-get rest :files)))
    `(progn
       (,@(cond ((or files in)
                 (when (and files (not (or (listp files) (stringp files))))
                   (user-error "associate! :files expects a string or list of strings"))
                 (let ((hook-name (intern (format "narf--init-mode-%s" mode))))

                                   (or ,(not files)
                                       (and (boundp ',mode)
                                            (not ,mode)
                                            (narf/project-has-files ,@(-list files))))
                                   (or (not ,pred)
                                       (funcall ,pred buffer-file-name)))
                          (,mode 1)))
                      ,@(if (and in (listp in))
                            (mapcar (lambda (h) `(add-hook ',h ',hook-name))
