]> git.eshelyaron.com Git - emacs.git/commitdiff
Add 'case-symbols-as-words' to configure symbol case behavior
authorSpencer Baugh <sbaugh@catern.com>
Sat, 21 Oct 2023 15:09:39 +0000 (11:09 -0400)
committerEli Zaretskii <eliz@gnu.org>
Sun, 29 Oct 2023 11:32:43 +0000 (13:32 +0200)
In some programming languages and styles, a symbol (or every
symbol in a sequence of symbols) might be capitalized, but the
individual words making up the symbol should never be capitalized.

For example, in OCaml, type names Look_like_this and variable names
look_like_this, but it is basically never correct for something to
Look_Like_This.  And one might have "aa_bb cc_dd ee_ff" or "Aa_bb
Cc_dd Ee_ff", but never "Aa_Bb Cc_Dd Ee_Ff".

To support this, the new variable 'case-symbols-as-words' causes
symbol constituents to be treated as part of words only for case
operations.

* src/casefiddle.c (case_ch_is_word): New function.
(case_character_impl, case_character): Use 'case_ch_is_word'.
(syms_of_casefiddle): Define 'case-symbols-as-words'.
* src/search.c (Freplace_match): Use 'case-symbols-as-words'
when calculating case pattern.
* test/src/casefiddle-tests.el (casefiddle-tests--check-syms)
(casefiddle-case-symbols-as-words): Test 'case-symbols-as-words'.
* etc/NEWS: Announce 'case-symbols-as-words'.
* doc/lispref/strings.texi (Case Conversion): Document
'case-symbols-as-words'.
(Bug#66614)

doc/lispref/strings.texi
etc/NEWS
src/casefiddle.c
src/search.c
test/src/casefiddle-tests.el

index 7d11db49def48524ed6f231f4a339284ff8a2b60..665d4f9a8dc9ea0cf798d38bc75df3c4a21068cb 100644 (file)
@@ -1510,7 +1510,9 @@ case.
 
 The definition of a word is any sequence of consecutive characters that
 are assigned to the word constituent syntax class in the current syntax
-table (@pxref{Syntax Class Table}).
+table (@pxref{Syntax Class Table}), or if @code{case-symbols-as-words}
+is non-nil, also characters assigned to the symbol constituent syntax
+class.
 
 When @var{string-or-char} is a character, this function does the same
 thing as @code{upcase}.
@@ -1542,7 +1544,9 @@ had its initial letter converted to upper case.
 
 The definition of a word is any sequence of consecutive characters that
 are assigned to the word constituent syntax class in the current syntax
-table (@pxref{Syntax Class Table}).
+table (@pxref{Syntax Class Table}), or if @code{case-symbols-as-words}
+is non-nil, also characters assigned to the symbol constituent syntax
+class.
 
 When the argument to @code{upcase-initials} is a character,
 @code{upcase-initials} has the same result as @code{upcase}.
index ed9f1a2124cb652686190fe0376212b5ea525659..269346b591754245a609295203d7d25b2db4494e 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1193,6 +1193,14 @@ instead of "ctags", "ebrowse", "etags", "hexl", "emacsclient", and
 "rcs2log", when starting one of these built in programs in a
 subprocess.
 
++++
+** New variable 'case-symbols-as-words' affects case operations for symbols.
+If non-nil, then case operations such as 'upcase-initials' or
+'replace-match' (with nil FIXEDCASE) will treat the entire symbol name
+as a single word.  This is useful for programming languages and styles
+where only the first letter of a symbol's name is ever capitalized.
+It defaults to nil.
+
 +++
 ** 'x-popup-menu' now understands touch screen events.
 When a 'touchscreen-begin' or 'touchscreen-end' event is passed as the
index d567a5e353a7aaeed480708c2975344b1014ad22..3afb131c50e8cb096d6393d326d073b0662e6df0 100644 (file)
@@ -92,6 +92,12 @@ prepare_casing_context (struct casing_context *ctx,
     SETUP_BUFFER_SYNTAX_TABLE ();      /* For syntax_prefix_flag_p.  */
 }
 
+static bool
+case_ch_is_word (enum syntaxcode syntax)
+{
+  return syntax == Sword || (case_symbols_as_words && syntax == Ssymbol);
+}
+
 struct casing_str_buf
 {
   unsigned char data[max (6, MAX_MULTIBYTE_LENGTH)];
@@ -115,7 +121,7 @@ case_character_impl (struct casing_str_buf *buf,
 
   /* Update inword state */
   bool was_inword = ctx->inword;
-  ctx->inword = SYNTAX (ch) == Sword &&
+  ctx->inword = case_ch_is_word (SYNTAX (ch)) &&
     (!ctx->inbuffer || was_inword || !syntax_prefix_flag_p (ch));
 
   /* Normalize flag so its one of CASE_UP, CASE_DOWN or CASE_CAPITALIZE.  */
@@ -222,7 +228,7 @@ case_character (struct casing_str_buf *buf, struct casing_context *ctx,
      has a word syntax (i.e. current character is end of word), use final
      sigma.  */
   if (was_inword && ch == GREEK_CAPITAL_LETTER_SIGMA && changed
-      && (!next || SYNTAX (STRING_CHAR (next)) != Sword))
+      && (!next || !case_ch_is_word (SYNTAX (STRING_CHAR (next)))))
     {
       buf->len_bytes = CHAR_STRING (GREEK_SMALL_LETTER_FINAL_SIGMA, buf->data);
       buf->len_chars = 1;
@@ -720,6 +726,21 @@ Called with one argument METHOD which can be:
   3rd argument.  */);
   Vregion_extract_function = Qnil; /* simple.el sets this.  */
 
+  DEFVAR_BOOL ("case-symbols-as-words", case_symbols_as_words,
+    doc: /* If non-nil, case functions treat symbol syntax as part of words.
+
+Functions such as `upcase-initials' and `replace-match' check or modify
+the case pattern of sequences of characters.  Normally, these operate on
+sequences of characters whose syntax is word constituent.  If this
+variable is non-nil, then they operate on sequences of characters whose
+syntax is either word constituent or symbol constituent.
+
+This is useful for programming languages and styles where only the first
+letter of a symbol's name is ever capitalized.*/);
+  case_symbols_as_words = 0;
+  DEFSYM (Qcase_symbols_as_words, "case-symbols-as-words");
+  Fmake_variable_buffer_local (Qcase_symbols_as_words);
+
   defsubr (&Supcase);
   defsubr (&Sdowncase);
   defsubr (&Scapitalize);
index e9b29bb71793b7b07558b5eb4eae7fb1a1dc058b..692d848804970fa7b787d24f20705c7855c981d9 100644 (file)
@@ -2365,7 +2365,7 @@ text has only capital letters and has at least one multiletter word,
 convert NEWTEXT to all caps.  Otherwise if all words are capitalized
 in the replaced text, capitalize each word in NEWTEXT.  Note that
 what exactly is a word is determined by the syntax tables in effect
-in the current buffer.
+in the current buffer, and the variable `case-symbols-as-words'.
 
 If optional third arg LITERAL is non-nil, insert NEWTEXT literally.
 Otherwise treat `\\' as special:
@@ -2479,7 +2479,8 @@ since only regular expressions have distinguished subexpressions.  */)
              /* Cannot be all caps if any original char is lower case */
 
              some_lowercase = 1;
-             if (SYNTAX (prevc) != Sword)
+             if (SYNTAX (prevc) != Sword
+                 && !(case_symbols_as_words && SYNTAX (prevc) == Ssymbol))
                some_nonuppercase_initial = 1;
              else
                some_multiletter_word = 1;
@@ -2487,7 +2488,8 @@ since only regular expressions have distinguished subexpressions.  */)
          else if (uppercasep (c))
            {
              some_uppercase = 1;
-             if (SYNTAX (prevc) != Sword)
+             if (SYNTAX (prevc) != Sword
+                 && !(case_symbols_as_words && SYNTAX (prevc) == Ssymbol))
                ;
              else
                some_multiletter_word = 1;
@@ -2496,7 +2498,8 @@ since only regular expressions have distinguished subexpressions.  */)
            {
              /* If the initial is a caseless word constituent,
                 treat that like a lowercase initial.  */
-             if (SYNTAX (prevc) != Sword)
+             if (SYNTAX (prevc) != Sword
+                 && !(case_symbols_as_words && SYNTAX (prevc) == Ssymbol))
                some_nonuppercase_initial = 1;
            }
 
index e7f4348b0c6f8448e30e41e518c051d1662fd5f8..12984d898b95aaa9017ffff9e476a2ab7cecaa4b 100644 (file)
     ;;(should (string-equal (capitalize "indIá") "İndıa"))
     ))
 
+(defun casefiddle-tests--check-syms (init with-words with-symbols)
+  (let ((case-symbols-as-words nil))
+    (should (string-equal (upcase-initials init) with-words)))
+  (let ((case-symbols-as-words t))
+    (should (string-equal (upcase-initials init) with-symbols))))
+
+(ert-deftest casefiddle-case-symbols-as-words ()
+  (casefiddle-tests--check-syms "Aa_bb Cc_dd" "Aa_Bb Cc_Dd" "Aa_bb Cc_dd")
+  (casefiddle-tests--check-syms "Aa_bb cc_DD" "Aa_Bb Cc_DD" "Aa_bb Cc_DD")
+  (casefiddle-tests--check-syms "aa_bb cc_dd" "Aa_Bb Cc_Dd" "Aa_bb Cc_dd")
+  (casefiddle-tests--check-syms "Aa_Bb Cc_Dd" "Aa_Bb Cc_Dd" "Aa_Bb Cc_Dd"))
+
 ;;; casefiddle-tests.el ends here