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
Use 'string-match-p' instead of 'string-match' where applicable #1517
Conversation
ivy.el
Outdated
@@ -910,14 +910,14 @@ If the text hasn't changed as a result, forward to `ivy-alt-done'." | |||
(postfix (car (last parts))) | |||
(case-fold-search (ivy--case-fold-p ivy-text)) | |||
(completion-ignore-case case-fold-search) | |||
(startp (string-match "^\\^" postfix)) | |||
(startp (string-matc-ph "^\\^" postfix)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/matc-ph/match-p/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, thanks!
ivy.el
Outdated
@@ -1963,14 +1963,14 @@ This is useful for recursive `ivy-read'." | |||
(let ((w (length (number-to-string | |||
(length ivy--all-candidates)))) | |||
(s (copy-sequence ivy-count-format))) | |||
(string-match "%d" s) | |||
(string-match-p "%d" s) | |||
(match-end 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a no-op right? And it was in the code before your change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, that's the issue I was talking about. This is not a no-op and my change is incorrect: before the change string-match
modified global match data which was subsequently read by match-end
. The string-match-p
leaves match data intact, so match-end
now reads whatever was previously in that global structure. I'll revert this - very unprofessional of me, sorry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sergv One of the calls to match-end
is a no-op though, as its value isn't used anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, indeed.
ivy.el
Outdated
(match-end 0) | ||
(string-match "%d" s (match-end 0)) | ||
(string-match-p "%d" s (match-end 0)) | ||
(setq s (replace-match (format "%%-%dd" w) nil nil s)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't replace-match
use match-data
? I think this will introduce a bug. Can you confirm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not only replace-match
uses match-data
, but also the match-end
passed to the second string-match
from the first string-match
. So neither of the two string-match
es should be changed, at least at first glance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, my changes are wrong here.
Thanks for the contribution. Two questions:
|
Thanks for quick response! Per your questions:
(let ((re "a+\\(b*\\)c+")
(input (concat (make-string 100 ?a)
(make-string 100 ?b)
(make-string 100 ?c)))
(repeats 100000))
(eval
`(progn
(defun test1 ()
(garbage-collect)
(benchmark-run ,repeats
(string-match-p ,re ,input)))
(defun test2 ()
(garbage-collect)
(benchmark-run ,repeats
(string-match ,re ,input)))
;; Manually unpacked `string-match-p' that sets `inhibit-changing-match-data'
;; outside the benchmark loop.
(defun test3 ()
(garbage-collect)
(let ((inhibit-changing-match-data t))
(benchmark-run ,repeats
(string-match ,re ,input))))))
(byte-compile #'test1)
(byte-compile #'test2)
(byte-compile #'test3)
(list (list 'string-match-p (car (test1)))
(list 'string-match (car (test2)))
(list 'hacky (car (test3))))) Some sample runs:
Thus, given that
I propose to drop this change as pretty minor and close this PR. Sorry for inconvenience. |
One advantage of using the |
@rgrinberg That's very true. In addition, it's also important IMO to convey the correct intent to the reader, something I think comes up more frequently and has a greater chance of introducing subtle future bugs. |
Are you willing to get it? I can only merge changes up to 15 lines without a CA.
|
Are you willing to get it? I can only merge changes up to 15 lines without a CA.
Dropping it is an option, sure. But I do believe some small speed-up can be gained, and further more
No trouble at all, this can be a good change provided it doesn't introduce bugs. Maybe we can do it in smaller chunks to make sure no
Agreed. |
Okay, if there's consensus that this is a nice thing to have then lets keep the PR.I have rebased the branch and addressed the issues identified previously. I'll look into obtaining Emacs Copyright Assignment and get back to you. |
@sergv Thanks. |
@abo-abo I have received confirmation that my copyright assignment to the FSF is now complete. I believe we can merge now? |
ivy.el
Outdated
@@ -1965,14 +1965,12 @@ This is useful for recursive `ivy-read'." | |||
(let ((w (length (number-to-string | |||
(length ivy--all-candidates)))) | |||
(s (copy-sequence ivy-count-format))) | |||
(string-match "%d" s) | |||
(match-end 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The call to match-end
is indeed a no-op, but I'm not sure you can get rid of the call to string-match
; there's a call to match-end
just after these two forms.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
string-match
here is followed by another string-match
, which would overwrite match data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
string-match
here is followed by anotherstring-match
, which would overwrite match data.
Yes, but the second string-match
looks like it uses the match-end
of the first string-match
as its starting index.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have found a way to replace two string-match
es with a single one. Please take a look at Simplify ivy-add-prompt-count
commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have found a way to replace two
string-match
es with a single one.
Nice, but why stop there? :)
diff --git a/ivy.el b/ivy.el
index 193af0a..d154bbf 100644
--- a/ivy.el
+++ b/ivy.el
@@ -1987,17 +1987,11 @@ ivy-add-prompt-count
((null ivy-count-format)
(error
"`ivy-count-format' can't be nil. Set it to \"\" instead"))
- ((string-match "%d.*%d" ivy-count-format)
- (let ((w (length (number-to-string
- (length ivy--all-candidates))))
- (s (copy-sequence ivy-count-format)))
+ ((string-match "%d.*\\(%d\\)" ivy-count-format)
+ (let* ((w (1+ (floor (log (max 1 (length ivy--all-candidates)) 10))))
+ (s (replace-match (format "%%-%dd" w) t t ivy-count-format 1)))
(string-match "%d" s)
- (match-end 0)
- (string-match "%d" s (match-end 0))
- (setq s (replace-match (format "%%-%dd" w) nil nil s))
- (string-match "%d" s)
- (concat (replace-match (format "%%%dd" w) nil nil s)
- prompt)))
+ (concat (replace-match (format "%%%dd" w) t t s) prompt)))
((string-match "%.*d" ivy-count-format)
(concat ivy-count-format prompt))
(ivy--directory
This way, there is less searching, less code, way less string allocation, and you also get to fix the replace-match
arguments FIXEDCASE
and LITERAL
which shouldn't have been nil
to begin with.
ivy.el
Outdated
@@ -2955,13 +2953,13 @@ All CANDIDATES are assumed to match NAME." | |||
"Re-sort candidates by NAME. | |||
All CANDIDATES are assumed to match NAME. | |||
Prefix matches to NAME are put ahead of the list." | |||
(if (or (string-match "^\\^" name) (string= name "")) | |||
(if (or (string-match-p "^\\^" name) (string= name "")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the regexp here and in a couple of places further down be:
"\\`\\^"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, if there's only a single line in the name
string then there would be no difference. I don't have a strong opinion in this case so I left it as is since it appears to be working OK.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, if there's only a single line in the
name
string then there would be no difference.
Yes, but if this code shouldn't be matching line beginnings then it shouldn't be matching line beginnings. My question is whether this code should be matching line beginnings. :)
ivy.el
Outdated
@@ -3831,7 +3829,7 @@ Don't finish completion." | |||
(interactive) | |||
(delete-minibuffer-contents) | |||
(if (and ivy--directory | |||
(string-match "/$" (ivy-state-current ivy-last))) | |||
(string-match-p "/$" (ivy-state-current ivy-last))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the regexp here be:
"/\\'"
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As with replacement of ^
, there is no difference if we're matching against a single line. I don't have a strong opinion on the matter. Do you feel it should be addressed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you feel it should be addressed?
Unless I'm missing something, then yes, because newlines are valid filename constituents. This can obviously be done as a separate commit or PR; I'm just drawing attention to it while we're here.
What's the status on this? |
@basil-conto I've replaced "^" by "\`" and "$" by "\'", rebased the branch and I also got the Copyright Assignment. I think the PR can be merged now. |
ivy.el
Outdated
@@ -3918,7 +3913,7 @@ Don't finish completion." | |||
(interactive) | |||
(delete-minibuffer-contents) | |||
(if (and ivy--directory | |||
(string-match "/$" (ivy-state-current ivy-last))) | |||
(string-match-p "/\\'" (ivy-state-current ivy-last))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I forgot, there's ivy--dirname-p
for this.
ivy.el
Outdated
(length ivy--all-candidates)))) | ||
(s (copy-sequence ivy-count-format))) | ||
((string-match "%d.*\\(%d\\)" ivy-count-format) | ||
(let ((w (1+ (floor (log (max 1 (length ivy--all-candidates)) 10)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be let*
.
ivy.el
Outdated
(s (copy-sequence ivy-count-format))) | ||
((string-match "%d.*\\(%d\\)" ivy-count-format) | ||
(let ((w (1+ (floor (log (max 1 (length ivy--all-candidates)) 10)))) | ||
(s (replace-match (format "%%-%dd" w) t t s 1))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fourth argument to replace-match
should be ivy-count-format
.
Apart from my last few comments, LGTM. |
@basil-conto Thanks, I've addressed all of your comments. |
@basil-conto I think it's fine to merge once the conflicts are resolved. |
@abo-abo @basil-conto I've rebased the branch. Please merge. |
Thanks. |
This should cause Emacs to do a little bit less work when matching regexps (judging from C code).
PS Although I tried to not use
string-match-p
if match data is subsequently accessed, I may have missed something. Please do re-verify that everything's ok before merging.