Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiline colouration #1888

Closed
wants to merge 5 commits into from
Closed

Multiline colouration #1888

wants to merge 5 commits into from

Conversation

Ambrevar
Copy link
Member

@Ambrevar Ambrevar commented Oct 6, 2017

Don't merge yet!

I've implemented multiline colouration by re-using the work you did.
I'll keep experimenting and foolproofing the code.

In the meantime, could you have a look at my changes? Please question anything you find dubious.
I am discovering new grounds here, so any guidance is more than welcome :)

@alphapapa
Copy link
Member

@Ambrevar Please test this with helm-org-rifle, as it depends on the separator and multi-line candidates. Thanks.

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Oct 7, 2017 via email

@Ambrevar
Copy link
Member Author

Ambrevar commented Oct 9, 2017

@alphapapa: I've tested against helm-show-kill-ring. Why helm-org-rifle in particular?

@Ambrevar
Copy link
Member Author

Ambrevar commented Oct 9, 2017

I've updated the changes:

  • The colours have now their own faces.
  • Movements are working properly it seems.

I think the existing code is overly complicated and I'm afraid my patch is not helping. A few suggestions of what we could do:

  • Remove the separator code if you agree that colour alternation is superior.
  • Untangled the nested function calls.

@Ambrevar
Copy link
Member Author

Ambrevar commented Oct 9, 2017

@thierryvolpiatto
In your original patch, you added this:

             do (let ((beg (point)))
                  (helm-insert-match m 'insert count source)
                  (put-text-property beg (point) 'helm-candidate
                                     (if (cl-oddp count) 'odd 'even)))

Why putting odd/even properties on singlelines?

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Oct 9, 2017 via email

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Oct 9, 2017 via email

@Ambrevar
Copy link
Member Author

Ambrevar commented Oct 9, 2017

Why two faces, only one face seems enough

Well, one for odd lines, one for even lines... Did you mean something else?

I think we are misunderstood in what we want to achieve: you want sources to define how they display (separator or not) through the slot.
I don't see the point in enforcing appearance to the user. I want to be able to use helm-show-kill-ring with colors and not separators.

What we have to do on this issue is first merge the original code (fix_multiline branch) again into master and see what's wrong with and without multiline, when this code will be fixed let's apply your patch on top of this.

Well, that's what I did: first commit re-applied your changes, second one fixes them (to the extend I've tested) and adds the variables/faces.

@alphapapa
Copy link
Member

I've tested against helm-show-kill-ring. Why helm-org-rifle in particular?

@Ambrevar Because it uses the separators and multi-line candidates in a less-common way. I would like for it to not be broken by this patch.

@Ambrevar
Copy link
Member Author

Don't remember the details, but I guess we need the property on single
line candidates as well because they should be present among
other multiline candidates.

The singleline is not a property of candidates but of the source. A singleline source will never need colors I believe.
As a matter of fact, the code does not deal with separators, so why should it deal with colors?

@Ambrevar
Copy link
Member Author

@alphapapa: Just tested with helm-org-rifle and it seems to work without an itch.

@Ambrevar
Copy link
Member Author

Why two faces, only one face seems enough
Well, one for odd lines, one for even lines... Did you mean something else?

Duh, I guess you meant we can leave odd entries to the normal background color and only color even entries. Right? I think it's much better indeed.
I suggest dark gray and light gray for dark background and light background respectively.

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Oct 10, 2017 via email

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Oct 10, 2017 via email

@alphapapa
Copy link
Member

@Ambrevar Thank you for testing it with helm-org-rifle. :)

Forgive me if I missed this in the conversation, but please take care to not require applying the alternate colors. For example, helm-org-rifle would not need that, and it would cause problems if it were enabled.

@Ambrevar
Copy link
Member Author

It does not, in fact it works with colors too, albeit useless since helm-org-rifle is using its own separator.

As the maintainer of helm-org-rifle, why don't you want to follow upstream? I understand you like empty separators better, but ultimately I believe that choice should be left to the user.

@Ambrevar
Copy link
Member Author

I've removed the colouration of odd multilines.

@Ambrevar Ambrevar changed the title DRAFT: Multiline coloration Multiline colouration Oct 11, 2017
@alphapapa
Copy link
Member

As the maintainer of helm-org-rifle, why don't you want to follow upstream?

I'm not sure why you would think that I don't want to follow upstream. I've worked with Thierry and contributed patches to Helm myself.

I understand you like empty separators better, but ultimately I believe that choice should be left to the user.

When I first created helm-org-rifle, the default Helm multi-line-candidate separator was a plain string of red ----------. It didn't look good in helm-org-rifle results, especially because each result is a propertized Org entry with fontified heading, which provides its own visual separation, just like in an Org buffer. So I set the separator to an empty string.

In the helm-org-rifle-occur commands, there is a separator, which is also a blank string, but is fontified with a customizable face. This is important since results in that command are persistent, can be manipulated, collapsed, deleted, etc.

This sounds like a nice change for most Helm commands, but I don't think it would be good to use it in helm-org-rifle, because its results are fontified the same way they appear in Org buffers, and this would interfere with that. So I hope that I won't have to work around this in my own code.

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Oct 12, 2017 via email

@Ambrevar
Copy link
Member Author

@alphapapa: Sorry for the misunderstanding, I did not mean to undermine your valuable contribution to Helm, far from that! :)
My question was specifically about the separator of helm-org-rifle which does not follow upstream. But you've answered that, so all good.

because its results are fontified the same way they appear in Org buffers, and this would interfere with that.

There is no interference: 1. The original code is still here. 2. My patch changes the background colours, thus this won't interfere with any of the usual org fontification.

@thierryvolpiatto: What is helm-multiline-separator?

@Ambrevar
Copy link
Member Author

@alphapapa:

It didn't look good in helm-org-rifle results, especially because each result is a propertized Org entry ? with fontified heading, which provides its own visual separation, just like in an Org buffer. So I set the separator to an empty string.

If headers are always starting entries, why not removing the separator altogether?

@alphapapa
Copy link
Member

My patch changes the background colours

That might be helpful in helm-org-rifle, so I look forward to trying that. The hard part may be getting the color right, since it depends on the user's theme. Is there a standard Emacs color for that? I don't think the standard highlight face would be a good choice, because, of course, that is how the currently selected item is shown.

If headers are always starting entries, why not removing the separator altogether?

Because it doesn't look good with them displayed so close together. This is especially so because headers are independent, so they may vary wildly in size, boldness, color, etc, and it looks confusing to have a small, deep heading displayed immediately before a large, shallow heading. Also, entries have entry text, not just heading text, so without a blank line, a line of entry text might be closer to the next heading than to its own heading.

Anyway, you can experiment with the code and remove it if you don't like it.

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Oct 13, 2017 via email

@Ambrevar
Copy link
Member Author

I can reproduce two issues:

  • cycle on, separator off: selection jumps back to beginning on some entries.

  • cycle on, seperator on: cycling backwards from first candidate to last, the last entry is not properly selected.

@Ambrevar
Copy link
Member Author

I've fixed both issues (first description was inacurate, see the commit).

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Oct 14, 2017 via email

@Ambrevar
Copy link
Member Author

I can't reproduce the performance issue.
I've tried with emacs-helm.sh and I get the same speed both on master and my branch.

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Oct 16, 2017 via email

@Ambrevar
Copy link
Member Author

OK, I can reproduce with 4000 candidates. Let's investigate.

@Ambrevar
Copy link
Member Author

Here is the profile I get when I fire up helm-list-elisp-packages-no-fetch and keep pressing C-n for a few seconds:

- command-execute                                                4088  93%
 - call-interactively                                            4088  93%
  - funcall-interactively                                        3967  90%
   - eval-last-sexp                                              3959  90%
    - elisp--eval-last-sexp                                      3959  90%
     - eval                                                      3959  90%
      - helm-list-elisp-packages-no-fetch                        3959  90%
       - helm-list-elisp-packages                                3959  90%
        - helm                                                   3959  90%
         - apply                                                 3959  90%
          - helm                                                 3959  90%
           - apply                                               3959  90%
            - helm-internal                                      3959  90%
             - helm-read-pattern-maybe                           3629  83%
              - read-from-minibuffer                             3433  78%
               - command-execute                                 3325  76%
                - call-interactively                             3325  76%
                 - funcall-interactively                         3247  74%
                  - helm-next-line                               3247  74%
                   - helm--next-or-previous-line                 3247  74%
                    - helm-move-selection-common                 3247  74%
                     - helm-mark-current-line                    1944  44%
                      - helm-get-next-candidate-separator-pos               1276  29%
                         helm-get-next-header-pos                 660  15%
                        helm-get-next-header-pos                  660  15%
                      - helm-window                                 4   0%
                       - helm-buffer-get                            4   0%
                          helm-action-window                        4   0%
                     - helm-move--next-line-fn                   1291  29%
                      - helm-get-next-candidate-separator-pos               1291  29%
                         helm-get-next-header-pos                 655  14%
                       helm-display-mode-line                       4   0%
                     + #<compiled 0x1d2bfab>                        4   0%
                 + byte-code                                       78   1%
               + redisplay_internal (C function)                   68   1%
              + helm-update                                       184   4%
                helm-get-candidate-number                           4   0%
             + helm-initialize                                    326   7%
             + #<compiled 0xc5c105>                                 4   0%
   + helm-M-x                                                       8   0%
  + byte-code                                                     121   2%
+ ...                                                             275   6%
+ redisplay_internal (C function)                                   4   0%

@Ambrevar
Copy link
Member Author

Same profiling procedure on master:

- command-execute                                                2916  91%
 - call-interactively                                            2916  91%
  - funcall-interactively                                        2761  86%
   - eval-last-sexp                                              2761  86%
    - elisp--eval-last-sexp                                      2761  86%
     - eval                                                      2761  86%
      - helm-list-elisp-packages-no-fetch                        2761  86%
       - helm-list-elisp-packages                                2761  86%
        - helm                                                   2761  86%
         - apply                                                 2761  86%
          - helm                                                 2761  86%
           - apply                                               2761  86%
            - helm-internal                                      2761  86%
             - helm-read-pattern-maybe                           2442  76%
              - read-from-minibuffer                             2272  70%
               + redisplay_internal (C function)                 1172  36%
               - command-execute                                    4   0%
                - call-interactively                                4   0%
                 - funcall-interactively                            4   0%
                  - helm-next-line                                  3   0%
                   - helm--next-or-previous-line                    3   0%
                    - helm-move-selection-common                    3   0%
                       helm-follow-execute-persistent-action-maybe                  3   0%
               + timer-event-handler                                2   0%
              + helm-update                                       142   4%
                helm-get-candidate-number                           8   0%
             + helm-initialize                                    313   9%
             + #<compiled 0x9efaf7>                                 6   0%
  + byte-code                                                     155   4%
+ ...                                                             288   8%

Revert "Revert aefc622 and all related changes since then"

This reverts commit a439b72.
It's enough for odd multilines to use the standard background colour.
This fixes two issues:

- With separator, cycling backward from the first entry would skip the last
entry.

- Without separator, moving upward to an entry without newlines would skip it.
Keep the multiline-related changes of aefc622 while using the control flow of
pre-aefc6227.
@Ambrevar
Copy link
Member Author

Ambrevar commented Oct 17, 2017

I've fixed the issue. I've rebased my changes over your master branch since there were some conflicts. The fix commit is on my master branch, but somehow GitHub does not see the new commit on this PR. Am I missing something or should I open a new PR?

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Oct 17, 2017 via email

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Oct 17, 2017 via email

@Ambrevar
Copy link
Member Author

Perhaps reload the github page ?

It took ages to refresh.

It is not fixed here it still unusable on large set of candidates.

I'll see if I can reproduce.

@Ambrevar
Copy link
Member Author

I cannot reproduce: it's really fast with my patch. Here is the profile:

- command-execute                                                3950  92%
 - call-interactively                                            3950  92%
  - funcall-interactively                                        3806  89%
   - eval-last-sexp                                              3802  89%
    - elisp--eval-last-sexp                                      3802  89%
     - eval                                                      3802  89%
      - helm-test-init                                           3802  89%
       - helm-list-elisp-packages-no-fetch                       3653  85%
        - helm-list-elisp-packages                               3653  85%
         - helm                                                  3653  85%
          - apply                                                3653  85%
           - helm                                                3653  85%
            - apply                                              3653  85%
             - helm-internal                                     3653  85%
              - helm-read-pattern-maybe                          3337  78%
               - read-from-minibuffer                            3146  74%
                - command-execute                                1590  37%
                 - call-interactively                            1590  37%
                  - funcall-interactively                        1590  37%
                   - helm-next-line                              1590  37%
                    - helm--next-or-previous-line                1590  37%
                     - helm-move-selection-common                1590  37%
                      - helm-move--next-line-fn                  1578  37%
                       - helm-get-next-candidate-separator-pos               1574  37%
                          helm-get-next-header-pos                810  19%
                        helm-display-mode-line                     12   0%
                + redisplay_internal (C function)                 973  22%
               + helm-update                                      156   3%
                 helm-get-candidate-number                         12   0%
              + helm-initialize                                   310   7%
              + #<compiled 0x90a613>                                6   0%
       + package-refresh-contents                                 147   3%
       + package-initialize                                         2   0%
   + helm-M-x                                                       4   0%
  + byte-code                                                     144   3%
+ ...                                                             300   7%

@Ambrevar
Copy link
Member Author

On master, redisplay_internal is the bottleneck at 36% while command-execute takes 0%.
With my patch, redisplay_internal takes 22% while command-execute takes 37%.

The slow part is seemingly around the new helm-get-next-candidate-separator-pos. I'll see how we can optimize this.

@Ambrevar
Copy link
Member Author

So the offending part is this:

(defun helm-move--next-line-fn ()
  (cond ((and (not (helm-pos-multiline-p))
              (helm-get-next-candidate-separator-pos))
        ((not (helm-pos-multiline-p))
         (helm--forward-candidate))
        (t (helm-move--next-multi-line-fn)))

And more specifically:

  (cond ((and (not (helm-pos-multiline-p))
              (helm-get-next-candidate-separator-pos))

While I understand the last 2 conditions, why the first one?
If this is not multiline, then there should be no separator, or am I mistaken?

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Oct 19, 2017 via email

@Ambrevar
Copy link
Member Author

Ambrevar commented Oct 19, 2017

That's a confusing name then :p

Can you detail the meaning of the algorithm? Here is what I understand:

Cond:

  • If not on a separator line (no cost) and there is a separator somewhere down below (scans the whole buffer, super costly O(n)), forward line. Why would we do that? Why the check and why forwarding line?

  • If not on a separator, go to next candidate. Makes sense.

  • If on a separator, move to next multiline. Makes sense.

Assuming first condition is really needed, a possible fix would be to check if it's a multiline source and we are last candidate. But I still don't get the point.

@Ambrevar
Copy link
Member Author

Ambrevar commented Nov 6, 2017

Any insight?

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Nov 6, 2017 via email

@Ambrevar
Copy link
Member Author

Ambrevar commented Nov 6, 2017

Can you explain the intention of the first condition mentioned above?

Also here is an optimization idea, no clue if that'd be faster though. When looking for the next separator,

(defun helm-get-next-candidate-separator-pos ()
  "Return the position of the next candidate separator from point."
  (let ((hp (helm-get-next-header-pos)))
    (helm-aif (next-single-property-change (point) 'helm-candidate-separator)

Instead of scanning all characters, why not scanning the first character of every line?
(Assuming separators can only be on their own line.)

That would probably involve Lisp code instead of C, so that could actually be slower.

@Ambrevar
Copy link
Member Author

Ambrevar commented Jun 8, 2018

Are you dropping this?
Sorry for the long radio-silence, but I was eventually thinking of giving it another shot at some point. What do you think?

@thierryvolpiatto
Copy link
Member

thierryvolpiatto commented Jun 9, 2018 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants