Skip to content

Latest commit

 

History

History
1055 lines (837 loc) · 44.2 KB

notes.org_archive

File metadata and controls

1055 lines (837 loc) · 44.2 KB

Archived entries from file /home/me/src/helm-org-rifle/notes.org

Matching symbol parts

;; This should return the "Target heading" heading too
(helm-org-rifle-get-candidates-in-buffer (find-file-noselect "test.org") "face")

(helm-org-rifle-get-candidates-in-buffer (find-file-noselect "~/org/inbox.org") "face helm")
(helm-org-rifle-get-candidates-in-buffer (find-file-noselect "testtemp.org") "face helm")

This does not work:

(let ((target "(face-remap-set-base 'helm-selection")
      (token "face"))
  (string-match (concat "\\_<" token "\\_>") target))

Which is strange, because \\_< is supposed to be the symbol-boundary character…

This works but isn’t what we want:

(let ((target "(face-remap-set-base 'helm-selection")
      (token "face"))
  (string-match token target))
(let ((target "(face-remap-set-base 'helm-selection")
      (token "face"))
  (string-match (concat "\\b" token "\\b") target))

This may do it:

(let ((target "(face-remap-set-base 'helm-selection")
      (token "face"))
  (string-match (concat "\\W" token "\\W") target))

It matches face okay, but not helm because of the quote.

(let ((target "(face-remap-set-base 'helm-selection")
      (token "helm"))
  (string-match (concat "\\(\\W\\|\\_<\\)" token "\\(\\W\\|\\_>\\)") target))

Wow, that “‘helm-selection” really doesn’t want to be matched…

This might do it…

(string-match "\\(\\B\\|\\W\\)face" "(face-remap-set-base 'helm-selection")

Seems to work… now for the real test…

(let ((target "(face-remap-set-base 'helm-selection")
      (token "selection"))
  (string-match (concat "\\(\\B\\|\\W\\)" token "\\(\\W\\|\\B\\)") target))

I think it works! Let’s try it for real…

Buuuuut it doesn’t match “selection”. Wow.

(let ((target "(face-remap-set-base 'helm-selection")
      (tokens '("face" "helm" "blah" "selection" "base")))
  (cl-loop for token in tokens
           when (string-match (concat "\\(\\B\\|\\W\\|\\_<\\|[[:punct:]]\\)" token "\\(\\B\\|\\W\\|\\_>\\|[[:punct:]]\\)") target)
           collect token))

Ok, this seems to match for “face”, “helm”, and “selection” and “base” but not “blah”.

Okay, this seems to work:

(defcustom helm-org-rifle-re-begin-part
  "\\(\\B\\|\\W\\|\\_<\\|[[:punct:]]\\)"
  "Argh"
  :group 'helm-org-rifle :type 'regexp)

(defcustom helm-org-rifle-re-end-part
  "\\(\\B\\|\\W\\|\\_>\\|[[:punct:]]\\)"
  "argh"
  :group 'helm-org-rifle :type 'regexp)

;; Then do:
(concat helm-org-rifle-re-begin-part token helm-org-rifle-re-end-part)

But it feels like it’s matching slower now, so I guess I need to experiment with different ones…

(let ((target "(face-remap-set-base 'helm-selection")
      (tokens '("face" "helm" "blah" "selection" "base")))
  (cl-loop for token in tokens
           when (string-match (concat helm-org-rifle-re-begin-part token helm-org-rifle-re-end-part) target)
           collect token))

Let’s try a simpler one:

(setq helm-org-rifle-re-begin-part
  "\\(\\B\\|\\_<\\|[[:punct:]]\\)")

(setq helm-org-rifle-re-end-part
  "\\(\\B\\|\\_>\\|[[:punct:]]\\)")

Ok, that works. Now for another:

(setq helm-org-rifle-re-begin-part
  "\\(\\_<\\|[[:punct:]]\\)")

(setq helm-org-rifle-re-end-part
  "\\(\\_>\\|[[:punct:]]\\)")

Ok, that seems to work too. Kind of makes sense: symbol boundaries or punctuation (which apparently doesn’t count as a symbol-boundary…for some values of syntax table…)

Ok, this seems to work and seems to be decently fast. Let’s commit it and try it out for a while.

Target heading

Searching for just the first word should find this, but it doesn’t; only searching for face-remap-set-base does.

:after-init-hook (lambda ()
                   (with-current-buffer helm-buffer
                     (face-remap-set-base 'helm-selection
                                          :underline 'unspecified
                                          :weight 'unspecified
                                          :background (face-attribute 'helm-selection :background))))

Order-sensitive matching

We want order to be irrelevant. So searching for “bravo alpha” should match the following subheading…

And it does. Except…

;; This works
(helm-org-rifle-get-candidates-in-buffer (get-file-buffer "~/org/inbox.org") "emacs org-mode")

;; This works
(helm-org-rifle-get-candidates-in-buffer (get-file-buffer "~/org/inbox.org") "org-mode emac")

;; This gives a weird args-out-of-range error.  Does it only happen in this large file?
(helm-org-rifle-get-candidates-in-buffer (get-file-buffer "~/org/inbox.org") "org-mode emacs")
(helm-org-rifle-get-candidates-in-buffer (get-file-buffer "~/org/reference.org") "org-mode emacs")

;; No, it happens in the smaller file too...are hyphens the problem?...yes...

Test entry

alpha bravo

Hyphenated words cause order-sensitive matching?

;; This works fine
(helm-org-rifle-get-candidates-in-buffer (current-buffer) "alpha charlie-delta")

;; So does this
(helm-org-rifle-get-candidates-in-buffer (current-buffer) "charlie-delta")

;; And this
(helm-org-rifle-get-candidates-in-buffer (current-buffer) "charlie-delta alpha")

;; But this does not!
(helm-org-rifle-get-candidates-in-buffer (current-buffer) "org-mode alpha")

;; But this works!
(helm-org-rifle-get-candidates-in-buffer (current-buffer) "org-mode blah")

The problem seems to be when the hyphenated word is on a different line than the non-hyphenated word (and we’re only dealing with two words here…). I sure don’t know why. Will have to step through the matching code…

ivy-regex-ignore-order

The ivy-regex-ignore-order setting in ivy/swiper might help with figuring this out.

Test entry

alpha bravo charlie-delta argh org-mode blah

Priority

Headings with priorities should be correctly displayed in results.

[#B] Priority target heading

Baby elephant

Match and show tags

  • State “DONE” from “UNDERWAY” [2016-03-28 Mon 19:34]
  • State “UNDERWAY” from “DONE” [2016-03-28 Mon 19:31]
  • State “DONE” from “TODO” [2016-03-28 Mon 17:30]

This should show both this heading and the target:

(let ((helm-org-rifle-show-tags t))
        (helm-org-rifle-get-candidates-in-buffer (current-buffer) "charade"))

This should show only this heading:

(let ((helm-org-rifle-show-tags nil))
      (helm-org-rifle-get-candidates-in-buffer (current-buffer) "charade"))

Target heading for tags test

Yarr.

Target heading 2

This should also match for the content: :charade:

Fontify tags correctly

Tags are being fontified just like the rest of the heading text, instead of like tags.

(helm-org-rifle-fontify-like-in-org-mode (s-join " " (list "*"
                                                           "Heading"
                                                           ":tag1:tag2:")))

Seems like there needs to be whitespace after the tag string to make it appear in the org-tag face.

(helm-org-rifle-fontify-like-in-org-mode (s-join " " (list "*"
                                                           "Heading"
                                                           ":tag1:tag2: ")))

Match with colons

Surrounding tags with colons in the input doesn’t seem to work:

(let ((helm-org-rifle-show-tags t))
        (helm-org-rifle-get-candidates-in-buffer (current-buffer) ":charade:"))

It’s because the colons in the tag string are being matched by the :punct: in the regexp’s first part, eating the colon so it doesn’t match the one in the input string. I’m not sure how to fix that. I guess I could make the matching regexp a series of prefix-input-suffix groups, and adjust the prefix and suffix for inputs that should match tags…seems messy but I guess it would work.

\(\_<\|[[:punct:]]\)\( \)\(\_>\|[[:punct:]]\)

"(_<|[[:punct:]])(:tag1:)(_>|[[:punct:]])"
(string-match "^:[[:word:]@:]+:$" ":charade:tag2:")
(string-match "a" "ba")
(let* ((input (split-string input " " t))
       ;; Double colons in tag strings in input so they can match
       (input (mapcar (lambda (s)
                        (if (string-match helm-org-rifle-tags-re s)
                            (replace-regexp-in-string ":" "::" s)
                          s))
                      input))
       (match-all-tokens-re (mapconcat (lambda (token)
                                         (if (string-match helm-org-rifle-tags-re token)
                                             ;; Remove punct class from prefix and suffix so it can match tag strings
                                             (concat "\\_<" (regexp-quote token) "\\_>")
                                           ;; Not a tag; use normal prefix/suffix
                                           (concat helm-org-rifle-re-begin-part
                                                   (regexp-quote token)
                                                   helm-org-rifle-re-end-part)))))
       ;; TODO: Turn off case folding if input contains mixed case
       (case-fold-search t)
       results))

Match headings with multiple tags

Now it matches headings with one tag, but not more than one.

(let ((helm-org-rifle-show-tags t))
        (helm-org-rifle-get-candidates-in-buffer (current-buffer) ":gunn:"))

Maybe this will help, from org.el

(org-re "\\(?:[ \t]+\\(:[[:alnum:]_@#%%:]+:\\)\\)?")
(let ((helm-org-rifle-show-tags t)
      (helm-org-rifle-tags-re (org-re "\\(?:[ \t]+\\(:[[:alnum:]_@#%%:]+:\\)\\)?")))
  (--map (substring-no-properties (car it))  (helm-org-rifle-get-candidates-in-buffer (current-buffer) ":gunn:")))

Yeah, using that regexp from org-complex-heading-regexp-format in org.el seems to work. Whew.

But this is the more correct one I think:

re in org.el:

(let ((tag-re (concat org-outline-regexp-bol
                      "\\(?:.*?[ \t]\\)?"
                      (org-re ":\\([[:alnum:]_@#%:]+\\):[ \t]*$")))
      (targets (list ":yes:" "no")))
  (mapcar (it (when (string-match tag-re it)
                (match-string 0 it))) targets))

And this more minimal one seems to work too:

(let ((tag-re (org-re ":\\([[:alnum:]_@#%:]+\\):[ \t]*$"))
      (targets (list ":yes:" "location" ":tag:" "notatag")))
  (mapcar (it (when (string-match tag-re it)
                (match-string 0 it))) targets))
(let ((tag-re helm-org-rifle-tags-re)
      (targets (list ":yes:" "location" ":tag:" "notatag" ":website:Emacs:")))
  (mapcar (it (when (string-match tag-re it)
                (match-string 0 it))) targets))

Target heading with multiple tags

Trying to fix it again

[2016-04-01 Fri 21:18] Here I go again… This code is from up above, the first time I implemented/fixed tag matching:

(let ((helm-org-rifle-show-tags t)
      (helm-org-rifle-tags-re (org-re "\\(?:[ \t]+\\(:[[:alnum:]_@#%%:]+:\\)\\)?")))
  (--map (substring-no-properties (car it))
         (helm-org-rifle-get-candidates-in-buffer (find-file-noselect "~/org/inbox.org")
                                                  ":website: Emacs")))

Ok, it seems to work now.

Note: Having this block of code (which I copied from further up) saved me probably hours of debugging. Somehow in the course of fixing the context-matching and re-fixing negation, I broke tag matching, and the key to fixing it again was having this:

(org-re "\\(?:[ \t]+\\(:[[:alnum:]_@#%%:]+:\\)\\)?")

…as opposed to this, which doesn’t seem to work:

(org-re ":\\([[:alnum:]_@#%:]+\\):[ \t]*$")

If I hadn’t kept these notes, I’d probably have spent at least another hour tracing the problem to the regexp and going back into org.el and finding the right one again. I’ve never kept notes like this about a programming project before, but I am completely sold on doing this now, especially with Org, which is so powerful with its inline evaluation, code block evaluation, intermingling of code with results with comments…

Negation

  • State “DONE” from “UNDERWAY” [2016-03-28 Mon 20:49]
  • State “UNDERWAY” from “DONE” [2016-03-28 Mon 18:57]
  • State “DONE” from “UNDERWAY” [2016-03-28 Mon 18:03]
  • State “UNDERWAY” from “” [2016-03-28 Mon 17:30]

Emacs regexps don’t support negation or lookahead, so we’ll have to do it in stages.

  1. Find negation patterns and move them to a separate list.
  2. Test each match against negations and remove any that match.

Find negation patterns

(mapcar (it (helm-org-rifle-prep-token it)) '("notatag" ":tag:"))
(mapit '("notatag" ":tag:") (string-match helm-org-rifle-tags-re it))
(let ((helm-org-rifle-tags-re (org-re ":\\([[:alnum:]_@#%:]+\\):[ \t]*$")))
  (mapit '("notatag" ":tag:") (string-match helm-org-rifle-tags-re it)))
(let* ((input (split-string "summertime !difficult easy" " " t))
       (negations (delq nil (mapcar (lambda (token)
                                      (when (string-match "^!" token)
                                        (setq input (remove token input))
                                        (helm-org-rifle-prep-token (s-chop-prefix "!" token))))
                                    input))))
  (list input negations))

Test negation

(let ((helm-org-rifle-show-tags t))
        (helm-org-rifle-get-candidates-in-buffer (current-buffer) "target !winter"))

Target positive: summertime easy

Target negative: summertime difficult

Avoid partial negation

e.g. searching for location !cat should not exclude results containing location.

This should return the Target positive: location heading:

(let ((helm-org-rifle-show-tags t))
  (mapit (helm-org-rifle-get-candidates-in-buffer (current-buffer) "location !ca ")
         (s-replace "\n" "" (s-collapse-whitespace (org-no-properties (car it))))))

This should not return that heading:

(let ((helm-org-rifle-show-tags t))
  (mapit (helm-org-rifle-get-candidates-in-buffer (current-buffer) "location !cat ")
         (s-replace "\n" "" (s-collapse-whitespace (org-no-properties (car it))))))
(let ((pat "\\bcat\\b")
      (targets '("a cat sleeps" "a catastrophe" "what")))
  (mapit targets (when (string-match pat it)(match-string 0 it))))

…sigh. Use s-matches not s-contains. Duh.

(let ((pat "\\(\\_<\\|[[:punct:]]\\)cat\\(\\_>\\|[[:punct:]]\\)")
      (target "a cat sleeps"))
  (s-matches? pat target))

Conclusion

[2016-03-28 Mon 20:38] Well, I think it’s working correctly now, but I’m not 100% sure. Time will tell. If it’s not, hopefully I’ll discover it or get some reports.

Target positive: location

notacatbutadog

Target negative: cat

Target negative 2

cat

Profile with/without negation

Without negation

(profile-rifle 10 (helm-org-rifle-get-candidates-in-buffer (find-file-noselect "~/org/inbox.org") "emacs helm"))

With negation

(profile-rifle 10 (helm-org-rifle-get-candidates-in-buffer (find-file-noselect "~/org/inbox.org") "emacs helm !mail"))

Avoid clearing results when bare “!” is entered

It seems awkward that all of the results disappear when a bare ! is entered. Even if you type quickly, they all disappear and then reappear. Should be possible to fix this…

(helm-org-rifle-get-candidates-in-buffer (find-file-noselect "~/org/inbox.org") "emacs helm !")
(helm-org-rifle-get-candidates-in-buffer (find-file-noselect "~/org/inbox.org") "emacs helm !org")

Fixed. Thanks to /u/washy9999 for the feedback!

Get list of candidates for “test.org” buffer

(helm-org-rifle-get-candidates-in-buffer (get-file-buffer "test.org") "pomegr blueberry")
(helm-org-rifle-get-candidates-in-buffer (get-file-buffer "test.org") "green")
(helm-org-rifle-get-candidates-in-buffer (get-file-buffer "test.org") "green blue")
(helm-org-rifle-get-candidates-in-buffer (get-file-buffer "test.org") "pomegr")

(helm-org-rifle-get-candidates-in-buffer (get-file-buffer "test.org") "helm food")

(let ((helm-candidate-separator " ")
      (helm-org-rifle-show-path t))
  (helm-org-rifle-get-candidates-in-buffer (get-file-buffer "test.org") "green blue"))

Other buffers

(let ((helm-org-rifle-fontify-headings nil))
  (helm-org-rifle-get-candidates-in-buffer (get-buffer "reference.org") "emacs"))

(helm-org-rifle-get-candidates-in-buffer (get-buffer "reference.org") "emacs")
(helm-org-rifle-get-candidates-in-buffer (get-buffer "main.org") "emacs")

(helm-org-rifle-get-candidates-in-buffer (get-buffer "main.org") "tires")

Open Helm session on current org buffers

(let ((helm-candidate-separator " "))
  (helm :sources (helm-org-rifle-get-sources)))

(let ((helm-candidate-separator " ")
      (helm-org-rifle-show-path t))
  (helm :sources (helm-org-rifle-get-sources)))

Without fontification

(let ((helm-candidate-separator " ")
      (helm-org-rifle-fontify-headings nil))
  (helm :sources (helm-org-rifle-get-sources)))

(let ((helm-candidate-separator " ")
      (helm-org-rifle-show-path t)
      (helm-org-rifle-fontify-headings nil))
  (helm :sources (helm-org-rifle-get-sources)))

elp profiling

(setq helm-org-rifle-show-path nil)
(setq helm-org-rifle-show-path t)

Testing with helm: before negation support

(setq argh-how-many-buffers 0)
(message "This many buffers: %s" argh-how-many-buffers)
(setq argh-how-many-times-getc-called 0)
(message "This many times: %s" argh-how-many-times-getc-called)
(progn
  (dolist (p '("helm-" "org-" "string-" "s-"))
    (elp-instrument-package p))
  (let ((helm-pattern "emacs helm"))
    (helm-org-rifle))
  (elp-results)
  (elp-restore-all)
  (buffer-substring-no-properties (point-min) (point-max)))

Results

helm-org-rifle-get-sources

This function is working correctly, returning the number of sources that it should.

helm-org-rifle-get-candidates-in-buffer

This function works correctly and pretty quickly.

helm-org-rifle-get-candidates-in-buffer called 10 times for each buffer

However, Helm seems to be calling this function, the :candidates function…sometimes 10 times per buffer, sometimes less, like this time where it did it 6 times for each buffer (input was “emacs”):

Evaluate this elisp code block on your system? (y or n) y
executing Elisp code block...
This many sources: 18
ARGH called for buffer: test.org
ARGH called for buffer: README.org\helm-org-rifle
ARGH called for buffer: inbox.org
ARGH called for buffer: README.org\org-bookmark-heading
ARGH called for buffer: main.org
ARGH called for buffer: school.org
ARGH called for buffer: sparky.org
ARGH called for buffer: prayers.org
ARGH called for buffer: calendar.org
ARGH called for buffer: log.org
ARGH called for buffer: people.org
ARGH called for buffer: bible.org
ARGH called for buffer: books.org
ARGH called for buffer: misc.org
ARGH called for buffer: posts.org
ARGH called for buffer: quotes.org
ARGH called for buffer: reference.org
ARGH called for buffer: research.org
ARGH called for buffer: test.org
ARGH called for buffer: README.org\helm-org-rifle
ARGH called for buffer: inbox.org
ARGH called for buffer: README.org\org-bookmark-heading
ARGH called for buffer: main.org
ARGH called for buffer: school.org
ARGH called for buffer: sparky.org
ARGH called for buffer: prayers.org
ARGH called for buffer: calendar.org
ARGH called for buffer: log.org
ARGH called for buffer: people.org
ARGH called for buffer: bible.org
ARGH called for buffer: books.org
ARGH called for buffer: misc.org
ARGH called for buffer: posts.org
ARGH called for buffer: quotes.org
ARGH called for buffer: reference.org
ARGH called for buffer: research.org
ARGH called for buffer: test.org
ARGH called for buffer: README.org\helm-org-rifle
ARGH called for buffer: inbox.org
ARGH called for buffer: README.org\org-bookmark-heading
ARGH called for buffer: main.org
ARGH called for buffer: school.org
ARGH called for buffer: sparky.org
ARGH called for buffer: prayers.org
ARGH called for buffer: calendar.org
ARGH called for buffer: log.org
ARGH called for buffer: people.org
ARGH called for buffer: bible.org
ARGH called for buffer: books.org
ARGH called for buffer: misc.org
ARGH called for buffer: posts.org
ARGH called for buffer: quotes.org
ARGH called for buffer: reference.org
ARGH called for buffer: research.org
ARGH called for buffer: test.org
ARGH called for buffer: README.org\helm-org-rifle
ARGH called for buffer: inbox.org
ARGH called for buffer: README.org\org-bookmark-heading
ARGH called for buffer: main.org
ARGH called for buffer: school.org
ARGH called for buffer: sparky.org
ARGH called for buffer: prayers.org
ARGH called for buffer: calendar.org
ARGH called for buffer: log.org
ARGH called for buffer: people.org
ARGH called for buffer: bible.org
ARGH called for buffer: books.org
ARGH called for buffer: misc.org
ARGH called for buffer: posts.org
ARGH called for buffer: quotes.org
ARGH called for buffer: reference.org
ARGH called for buffer: research.org
ARGH called for buffer: test.org
ARGH called for buffer: README.org\helm-org-rifle
ARGH called for buffer: inbox.org
ARGH called for buffer: README.org\org-bookmark-heading
ARGH called for buffer: main.org
ARGH called for buffer: school.org
ARGH called for buffer: sparky.org
ARGH called for buffer: prayers.org
ARGH called for buffer: calendar.org
ARGH called for buffer: log.org
ARGH called for buffer: people.org
ARGH called for buffer: bible.org
ARGH called for buffer: books.org
ARGH called for buffer: misc.org
ARGH called for buffer: posts.org
ARGH called for buffer: quotes.org
ARGH called for buffer: reference.org
ARGH called for buffer: research.org
ARGH called for buffer: test.org
ARGH called for buffer: README.org\helm-org-rifle
ARGH called for buffer: inbox.org
ARGH called for buffer: README.org\org-bookmark-heading
ARGH called for buffer: main.org
ARGH called for buffer: school.org
ARGH called for buffer: sparky.org
ARGH called for buffer: prayers.org
ARGH called for buffer: calendar.org
ARGH called for buffer: log.org
ARGH called for buffer: people.org
ARGH called for buffer: bible.org
ARGH called for buffer: books.org
ARGH called for buffer: misc.org
ARGH called for buffer: posts.org
ARGH called for buffer: quotes.org
ARGH called for buffer: reference.org
ARGH called for buffer: research.org
Code block evaluation complete.

Okay, I think I see what it’s doing: Helm is calling the candidates function once for every character that is typed, plus one more time. When I type emacs it calls it 6 times per buffer, and when I type e it calls it twice per buffer. The :delay works in that it doesn’t start getting candidates until that much time has elapsed after I’ve finished typing, but then it goes ahead and calls it for every character I typed, plus one.

Problem might be in helm-update or helm-process-delayed-sources

Testing without helm

  (progn
    (let ((buffers (remove-if 'helm-org-rifle-buffer-invisible-p (org-buffer-list nil t)))
          (string "emacs helm";; (read-from-minibuffer "Words: ")
                  ))
      (dolist (p '("helm-" "org-" "string-" "s-"))
        (elp-instrument-package p))
      (dolist (buffer buffers)
        (helm-org-rifle-get-candidates-in-buffer buffer string)))
    (elp-results)
    (elp-restore-all)
(buffer-substring-no-properties (point-min) (point-max)))