Subject: bug#13041: 24.2; diacritic-fold-search

From: "Drew Adams" <[email-masked]>
Date: Tue, 4 Dec 2012 12:12:57 -0800

> The function [Martin] came up with goes as below.
> (defun decomposed-string-lessp (string1 string2)
>    "Return t if STRING1 is decomposition-less than STRING2."
> ...

I know nothing about character composition and have not tested this with
anything but a few western accents.  But this seems like good stuff.

1. Assuming this or similar is added to Emacs (please do).  Please consider
modifying it to respect `case-fold-search'.  These modified lines do that.

(setq prop1 (get-char-code-property
              (if case-fold-search
                  (downcase (elt string1 index1))
                (elt string1 index1))
              'decomposition))

[Same thing for prop2 with string2 and index2.]

(let ((value (compare-strings compat1 0 nil
                              compat2 0 nil case-fold-search)))

2. In addition, consider updating `string-lessp' to be sensitive to a variable
such as this:

(defvar ignore-diacritics nil
  "Non-nil means ignore diacritics for string comparisons.")

With that, an alternative to hard-coding a call to `decomposed-string-lessp' is
to bind `ignore-diacritics' and use `string-lessp'.

A similar change could be made for `compare-strings': reflect the value of
`ignore-diacritics'.  Or since that function has made the choice to pass
case-sensitivity as a parameter instead of respecting `case-fold-search', pass
another parameter for diacritic sensitivity.

3. More general than #2 would be a function like this, which is sensitive to
both `ignore-diacritics' and `case-fold-search' (this assumes the change
suggested above in #1 for `decomposed-string-lessp').

(defun my-string-lessp (s1 s2)
  "..."
  (if ignore-diacritics
      (decomposed-string-lessp s1 s2)
    (when case-fold-search (setq s1  (upcase s1)
                                 s2  (upcase s2)))
    (string-lessp s1 s2)))

Dunno a good name for this.  It's too late to let `string-lessp' itself act like
this - that would break stuff.

4. Even better than hard-coding `case-fold-search' in `my-string-less-p' and
`decomposed-string-lessp' would be to have those functions be sensitive to a
variable such as this:

(defvar string-case-variable 'case-fold-search
  "Value is a case-sensitivity variable such as `case-fold-search'.
The values of that variable must be like those for `case-fold-search':
nil means case-sensitive, non-nil means case-insensitive.")

Code could then bind `string-case-variable' to, say, `(not
completion-ignore-case)' or to any other case-sensitivity controlling sexp, when
appropriate.

This would have the advantages offered by passing an explicit case-sensitivity
parameter, as in `compare-strings', but also the advantages of dynamic scope:
binding `string-case-var' to affect all comparisons within scope.

Comparers such as `(my-)string-lessp' are often used as arguments to
higher-order functions that treat them as (only) binary predicates, i.e.,
predicates where any additional parameters specifying case or diacritic
sensitivity are ignored.