diff --git a/admin/notes/emba b/admin/notes/emba index 1f87a99ddc0..cad7a2e121c 100644 --- a/admin/notes/emba +++ b/admin/notes/emba @@ -107,12 +107,11 @@ via "make bootstrap". In order to use the image, start a container like docker run --interactive --tty --env EMACS_EMBA_CI=1 --name emacs-inotify \ - emacs-inotify /bin/sh -i + emacs-inotify /bin/bash -i -In this container, change the current directory to "/checkout". Now -you can apply all commands known for Emacs, like +In this container, your working directory is "/checkout". Now you can +apply all commands known for Emacs, like - cd /checkout make -C test files-tests.log While this container runs, you can also access its filesystem from diff --git a/admin/notes/unicode b/admin/notes/unicode index 0e000365da7..31c850af8fd 100644 --- a/admin/notes/unicode +++ b/admin/notes/unicode @@ -22,6 +22,7 @@ Emacs uses the following files from the Unicode Character Database . confusables.txt . emoji-data.txt . emoji-zwj-sequences.txt + . emoji-variation-sequences.txt . emoji-sequences.txt . BidiCharacterTest.txt @@ -29,7 +30,7 @@ Emacs also uses the file emoji-test.txt which should be imported from the Unicode's Public/emoji/ directory, and IdnaMappingTable.txt from the Public/idna/ directory. -First, the first 14 files, emoji-test.txt and IdnaMappingTable.txt +First, the first 15 files, emoji-test.txt and IdnaMappingTable.txt need to be copied into admin/unidata/, and the file https://www.unicode.org/copyright.html should be copied over copyright.html in admin/unidata (some of them might need trailing @@ -142,6 +143,20 @@ generated for auto-composition-emoji-eligible-codepoints by admin/unidata/emoji-zwj.awk. Note that your emoji font might not have glyphs for the newest codepoints yet. +Visit "emoji-variation-sequences.txt", and run the following lisp +fragment to actually insert the described codepoints, then check that +all the text in parentheses displays correctly (it can be helpful to +have `glyphless-char-display-control' customized to show hex codes for +variation selectors). + +(save-excursion + (goto-char (point-min)) + (while (re-search-forward "^\\([0-9A-Z]+\\) \\([0-9A-Z]+\\).*(\\([^)]+\\))" nil t) + (let ((ch (string-to-number (match-string 1) 16)) + (sel (string-to-number (match-string 2) 16)) + (sp (match-string 3))) + (replace-match (format "%s %c%c " sp ch sel) nil nil nil 3)))) + Finally, etc/NEWS should be updated to announce the support for the new Unicode version. diff --git a/admin/unidata/emoji-variation-sequences.txt b/admin/unidata/emoji-variation-sequences.txt new file mode 100644 index 00000000000..f3396ada19d --- /dev/null +++ b/admin/unidata/emoji-variation-sequences.txt @@ -0,0 +1,723 @@ +# emoji-variation-sequences.txt +# Date: 2022-05-13, 21:54:24 GMT +# © 2022 Unicode®, Inc. +# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. +# For terms of use, see https://www.unicode.org/terms_of_use.html +# +# Emoji Variation Sequences for UTS #51 +# Used with Emoji Version 15.0 and subsequent minor revisions (if any) +# +# For documentation and usage, see https://www.unicode.org/reports/tr51 +# +0023 FE0E ; text style; # (1.1 #︎ ) NUMBER SIGN +0023 FE0F ; emoji style; # (1.1 #️ ) NUMBER SIGN +002A FE0E ; text style; # (1.1 *︎ ) ASTERISK +002A FE0F ; emoji style; # (1.1 *️ ) ASTERISK +0030 FE0E ; text style; # (1.1 0︎ ) DIGIT ZERO +0030 FE0F ; emoji style; # (1.1 0️ ) DIGIT ZERO +0031 FE0E ; text style; # (1.1 1︎ ) DIGIT ONE +0031 FE0F ; emoji style; # (1.1 1️ ) DIGIT ONE +0032 FE0E ; text style; # (1.1 2︎ ) DIGIT TWO +0032 FE0F ; emoji style; # (1.1 2️ ) DIGIT TWO +0033 FE0E ; text style; # (1.1 3︎ ) DIGIT THREE +0033 FE0F ; emoji style; # (1.1 3️ ) DIGIT THREE +0034 FE0E ; text style; # (1.1 4︎ ) DIGIT FOUR +0034 FE0F ; emoji style; # (1.1 4️ ) DIGIT FOUR +0035 FE0E ; text style; # (1.1 5︎ ) DIGIT FIVE +0035 FE0F ; emoji style; # (1.1 5️ ) DIGIT FIVE +0036 FE0E ; text style; # (1.1 6︎ ) DIGIT SIX +0036 FE0F ; emoji style; # (1.1 6️ ) DIGIT SIX +0037 FE0E ; text style; # (1.1 7︎ ) DIGIT SEVEN +0037 FE0F ; emoji style; # (1.1 7️ ) DIGIT SEVEN +0038 FE0E ; text style; # (1.1 8︎ ) DIGIT EIGHT +0038 FE0F ; emoji style; # (1.1 8️ ) DIGIT EIGHT +0039 FE0E ; text style; # (1.1 9︎ ) DIGIT NINE +0039 FE0F ; emoji style; # (1.1 9️ ) DIGIT NINE +00A9 FE0E ; text style; # (1.1 ©︎ ) COPYRIGHT SIGN +00A9 FE0F ; emoji style; # (1.1 ©️ ) COPYRIGHT SIGN +00AE FE0E ; text style; # (1.1 ®︎ ) REGISTERED SIGN +00AE FE0F ; emoji style; # (1.1 ®️ ) REGISTERED SIGN +203C FE0E ; text style; # (1.1 ‼︎ ) DOUBLE EXCLAMATION MARK +203C FE0F ; emoji style; # (1.1 ‼️ ) DOUBLE EXCLAMATION MARK +2049 FE0E ; text style; # (3.0 ⁉︎ ) EXCLAMATION QUESTION MARK +2049 FE0F ; emoji style; # (3.0 ⁉️ ) EXCLAMATION QUESTION MARK +2122 FE0E ; text style; # (1.1 ™︎ ) TRADE MARK SIGN +2122 FE0F ; emoji style; # (1.1 ™️ ) TRADE MARK SIGN +2139 FE0E ; text style; # (3.0 ℹ︎ ) INFORMATION SOURCE +2139 FE0F ; emoji style; # (3.0 ℹ️ ) INFORMATION SOURCE +2194 FE0E ; text style; # (1.1 ↔︎ ) LEFT RIGHT ARROW +2194 FE0F ; emoji style; # (1.1 ↔️ ) LEFT RIGHT ARROW +2195 FE0E ; text style; # (1.1 ↕︎ ) UP DOWN ARROW +2195 FE0F ; emoji style; # (1.1 ↕️ ) UP DOWN ARROW +2196 FE0E ; text style; # (1.1 ↖︎ ) NORTH WEST ARROW +2196 FE0F ; emoji style; # (1.1 ↖️ ) NORTH WEST ARROW +2197 FE0E ; text style; # (1.1 ↗︎ ) NORTH EAST ARROW +2197 FE0F ; emoji style; # (1.1 ↗️ ) NORTH EAST ARROW +2198 FE0E ; text style; # (1.1 ↘︎ ) SOUTH EAST ARROW +2198 FE0F ; emoji style; # (1.1 ↘️ ) SOUTH EAST ARROW +2199 FE0E ; text style; # (1.1 ↙︎ ) SOUTH WEST ARROW +2199 FE0F ; emoji style; # (1.1 ↙️ ) SOUTH WEST ARROW +21A9 FE0E ; text style; # (1.1 ↩︎ ) LEFTWARDS ARROW WITH HOOK +21A9 FE0F ; emoji style; # (1.1 ↩️ ) LEFTWARDS ARROW WITH HOOK +21AA FE0E ; text style; # (1.1 ↪︎ ) RIGHTWARDS ARROW WITH HOOK +21AA FE0F ; emoji style; # (1.1 ↪️ ) RIGHTWARDS ARROW WITH HOOK +231A FE0E ; text style; # (1.1 ⌚︎ ) WATCH +231A FE0F ; emoji style; # (1.1 ⌚️ ) WATCH +231B FE0E ; text style; # (1.1 ⌛︎ ) HOURGLASS +231B FE0F ; emoji style; # (1.1 ⌛️ ) HOURGLASS +2328 FE0E ; text style; # (1.1 ⌨︎ ) KEYBOARD +2328 FE0F ; emoji style; # (1.1 ⌨️ ) KEYBOARD +23CF FE0E ; text style; # (4.0 ⏏︎ ) EJECT SYMBOL +23CF FE0F ; emoji style; # (4.0 ⏏️ ) EJECT SYMBOL +23E9 FE0E ; text style; # (6.0 ⏩︎ ) BLACK RIGHT-POINTING DOUBLE TRIANGLE +23E9 FE0F ; emoji style; # (6.0 ⏩️ ) BLACK RIGHT-POINTING DOUBLE TRIANGLE +23EA FE0E ; text style; # (6.0 ⏪︎ ) BLACK LEFT-POINTING DOUBLE TRIANGLE +23EA FE0F ; emoji style; # (6.0 ⏪️ ) BLACK LEFT-POINTING DOUBLE TRIANGLE +23ED FE0E ; text style; # (6.0 ⏭︎ ) BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR +23ED FE0F ; emoji style; # (6.0 ⏭️ ) BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR +23EE FE0E ; text style; # (6.0 ⏮︎ ) BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR +23EE FE0F ; emoji style; # (6.0 ⏮️ ) BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR +23EF FE0E ; text style; # (6.0 ⏯︎ ) BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR +23EF FE0F ; emoji style; # (6.0 ⏯️ ) BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR +23F1 FE0E ; text style; # (6.0 ⏱︎ ) STOPWATCH +23F1 FE0F ; emoji style; # (6.0 ⏱️ ) STOPWATCH +23F2 FE0E ; text style; # (6.0 ⏲︎ ) TIMER CLOCK +23F2 FE0F ; emoji style; # (6.0 ⏲️ ) TIMER CLOCK +23F3 FE0E ; text style; # (6.0 ⏳︎ ) HOURGLASS WITH FLOWING SAND +23F3 FE0F ; emoji style; # (6.0 ⏳️ ) HOURGLASS WITH FLOWING SAND +23F8 FE0E ; text style; # (7.0 ⏸︎ ) DOUBLE VERTICAL BAR +23F8 FE0F ; emoji style; # (7.0 ⏸️ ) DOUBLE VERTICAL BAR +23F9 FE0E ; text style; # (7.0 ⏹︎ ) BLACK SQUARE FOR STOP +23F9 FE0F ; emoji style; # (7.0 ⏹️ ) BLACK SQUARE FOR STOP +23FA FE0E ; text style; # (7.0 ⏺︎ ) BLACK CIRCLE FOR RECORD +23FA FE0F ; emoji style; # (7.0 ⏺️ ) BLACK CIRCLE FOR RECORD +24C2 FE0E ; text style; # (1.1 Ⓜ︎ ) CIRCLED LATIN CAPITAL LETTER M +24C2 FE0F ; emoji style; # (1.1 Ⓜ️ ) CIRCLED LATIN CAPITAL LETTER M +25AA FE0E ; text style; # (1.1 ▪︎ ) BLACK SMALL SQUARE +25AA FE0F ; emoji style; # (1.1 ▪️ ) BLACK SMALL SQUARE +25AB FE0E ; text style; # (1.1 ▫︎ ) WHITE SMALL SQUARE +25AB FE0F ; emoji style; # (1.1 ▫️ ) WHITE SMALL SQUARE +25B6 FE0E ; text style; # (1.1 ▶︎ ) BLACK RIGHT-POINTING TRIANGLE +25B6 FE0F ; emoji style; # (1.1 ▶️ ) BLACK RIGHT-POINTING TRIANGLE +25C0 FE0E ; text style; # (1.1 ◀︎ ) BLACK LEFT-POINTING TRIANGLE +25C0 FE0F ; emoji style; # (1.1 ◀️ ) BLACK LEFT-POINTING TRIANGLE +25FB FE0E ; text style; # (3.2 ◻︎ ) WHITE MEDIUM SQUARE +25FB FE0F ; emoji style; # (3.2 ◻️ ) WHITE MEDIUM SQUARE +25FC FE0E ; text style; # (3.2 ◼︎ ) BLACK MEDIUM SQUARE +25FC FE0F ; emoji style; # (3.2 ◼️ ) BLACK MEDIUM SQUARE +25FD FE0E ; text style; # (3.2 ◽︎ ) WHITE MEDIUM SMALL SQUARE +25FD FE0F ; emoji style; # (3.2 ◽️ ) WHITE MEDIUM SMALL SQUARE +25FE FE0E ; text style; # (3.2 ◾︎ ) BLACK MEDIUM SMALL SQUARE +25FE FE0F ; emoji style; # (3.2 ◾️ ) BLACK MEDIUM SMALL SQUARE +2600 FE0E ; text style; # (1.1 ☀︎ ) BLACK SUN WITH RAYS +2600 FE0F ; emoji style; # (1.1 ☀️ ) BLACK SUN WITH RAYS +2601 FE0E ; text style; # (1.1 ☁︎ ) CLOUD +2601 FE0F ; emoji style; # (1.1 ☁️ ) CLOUD +2602 FE0E ; text style; # (1.1 ☂︎ ) UMBRELLA +2602 FE0F ; emoji style; # (1.1 ☂️ ) UMBRELLA +2603 FE0E ; text style; # (1.1 ☃︎ ) SNOWMAN +2603 FE0F ; emoji style; # (1.1 ☃️ ) SNOWMAN +2604 FE0E ; text style; # (1.1 ☄︎ ) COMET +2604 FE0F ; emoji style; # (1.1 ☄️ ) COMET +260E FE0E ; text style; # (1.1 ☎︎ ) BLACK TELEPHONE +260E FE0F ; emoji style; # (1.1 ☎️ ) BLACK TELEPHONE +2611 FE0E ; text style; # (1.1 ☑︎ ) BALLOT BOX WITH CHECK +2611 FE0F ; emoji style; # (1.1 ☑️ ) BALLOT BOX WITH CHECK +2614 FE0E ; text style; # (4.0 ☔︎ ) UMBRELLA WITH RAIN DROPS +2614 FE0F ; emoji style; # (4.0 ☔️ ) UMBRELLA WITH RAIN DROPS +2615 FE0E ; text style; # (4.0 ☕︎ ) HOT BEVERAGE +2615 FE0F ; emoji style; # (4.0 ☕️ ) HOT BEVERAGE +2618 FE0E ; text style; # (4.1 ☘︎ ) SHAMROCK +2618 FE0F ; emoji style; # (4.1 ☘️ ) SHAMROCK +261D FE0E ; text style; # (1.1 ☝︎ ) WHITE UP POINTING INDEX +261D FE0F ; emoji style; # (1.1 ☝️ ) WHITE UP POINTING INDEX +2620 FE0E ; text style; # (1.1 ☠︎ ) SKULL AND CROSSBONES +2620 FE0F ; emoji style; # (1.1 ☠️ ) SKULL AND CROSSBONES +2622 FE0E ; text style; # (1.1 ☢︎ ) RADIOACTIVE SIGN +2622 FE0F ; emoji style; # (1.1 ☢️ ) RADIOACTIVE SIGN +2623 FE0E ; text style; # (1.1 ☣︎ ) BIOHAZARD SIGN +2623 FE0F ; emoji style; # (1.1 ☣️ ) BIOHAZARD SIGN +2626 FE0E ; text style; # (1.1 ☦︎ ) ORTHODOX CROSS +2626 FE0F ; emoji style; # (1.1 ☦️ ) ORTHODOX CROSS +262A FE0E ; text style; # (1.1 ☪︎ ) STAR AND CRESCENT +262A FE0F ; emoji style; # (1.1 ☪️ ) STAR AND CRESCENT +262E FE0E ; text style; # (1.1 ☮︎ ) PEACE SYMBOL +262E FE0F ; emoji style; # (1.1 ☮️ ) PEACE SYMBOL +262F FE0E ; text style; # (1.1 ☯︎ ) YIN YANG +262F FE0F ; emoji style; # (1.1 ☯️ ) YIN YANG +2638 FE0E ; text style; # (1.1 ☸︎ ) WHEEL OF DHARMA +2638 FE0F ; emoji style; # (1.1 ☸️ ) WHEEL OF DHARMA +2639 FE0E ; text style; # (1.1 ☹︎ ) WHITE FROWNING FACE +2639 FE0F ; emoji style; # (1.1 ☹️ ) WHITE FROWNING FACE +263A FE0E ; text style; # (1.1 ☺︎ ) WHITE SMILING FACE +263A FE0F ; emoji style; # (1.1 ☺️ ) WHITE SMILING FACE +2640 FE0E ; text style; # (1.1 ♀︎ ) FEMALE SIGN +2640 FE0F ; emoji style; # (1.1 ♀️ ) FEMALE SIGN +2642 FE0E ; text style; # (1.1 ♂︎ ) MALE SIGN +2642 FE0F ; emoji style; # (1.1 ♂️ ) MALE SIGN +2648 FE0E ; text style; # (1.1 ♈︎ ) ARIES +2648 FE0F ; emoji style; # (1.1 ♈️ ) ARIES +2649 FE0E ; text style; # (1.1 ♉︎ ) TAURUS +2649 FE0F ; emoji style; # (1.1 ♉️ ) TAURUS +264A FE0E ; text style; # (1.1 ♊︎ ) GEMINI +264A FE0F ; emoji style; # (1.1 ♊️ ) GEMINI +264B FE0E ; text style; # (1.1 ♋︎ ) CANCER +264B FE0F ; emoji style; # (1.1 ♋️ ) CANCER +264C FE0E ; text style; # (1.1 ♌︎ ) LEO +264C FE0F ; emoji style; # (1.1 ♌️ ) LEO +264D FE0E ; text style; # (1.1 ♍︎ ) VIRGO +264D FE0F ; emoji style; # (1.1 ♍️ ) VIRGO +264E FE0E ; text style; # (1.1 ♎︎ ) LIBRA +264E FE0F ; emoji style; # (1.1 ♎️ ) LIBRA +264F FE0E ; text style; # (1.1 ♏︎ ) SCORPIUS +264F FE0F ; emoji style; # (1.1 ♏️ ) SCORPIUS +2650 FE0E ; text style; # (1.1 ♐︎ ) SAGITTARIUS +2650 FE0F ; emoji style; # (1.1 ♐️ ) SAGITTARIUS +2651 FE0E ; text style; # (1.1 ♑︎ ) CAPRICORN +2651 FE0F ; emoji style; # (1.1 ♑️ ) CAPRICORN +2652 FE0E ; text style; # (1.1 ♒︎ ) AQUARIUS +2652 FE0F ; emoji style; # (1.1 ♒️ ) AQUARIUS +2653 FE0E ; text style; # (1.1 ♓︎ ) PISCES +2653 FE0F ; emoji style; # (1.1 ♓️ ) PISCES +265F FE0E ; text style; # (1.1 ♟︎ ) BLACK CHESS PAWN +265F FE0F ; emoji style; # (1.1 ♟️ ) BLACK CHESS PAWN +2660 FE0E ; text style; # (1.1 ♠︎ ) BLACK SPADE SUIT +2660 FE0F ; emoji style; # (1.1 ♠️ ) BLACK SPADE SUIT +2663 FE0E ; text style; # (1.1 ♣︎ ) BLACK CLUB SUIT +2663 FE0F ; emoji style; # (1.1 ♣️ ) BLACK CLUB SUIT +2665 FE0E ; text style; # (1.1 ♥︎ ) BLACK HEART SUIT +2665 FE0F ; emoji style; # (1.1 ♥️ ) BLACK HEART SUIT +2666 FE0E ; text style; # (1.1 ♦︎ ) BLACK DIAMOND SUIT +2666 FE0F ; emoji style; # (1.1 ♦️ ) BLACK DIAMOND SUIT +2668 FE0E ; text style; # (1.1 ♨︎ ) HOT SPRINGS +2668 FE0F ; emoji style; # (1.1 ♨️ ) HOT SPRINGS +267B FE0E ; text style; # (3.2 ♻︎ ) BLACK UNIVERSAL RECYCLING SYMBOL +267B FE0F ; emoji style; # (3.2 ♻️ ) BLACK UNIVERSAL RECYCLING SYMBOL +267E FE0E ; text style; # (4.1 ♾︎ ) PERMANENT PAPER SIGN +267E FE0F ; emoji style; # (4.1 ♾️ ) PERMANENT PAPER SIGN +267F FE0E ; text style; # (4.1 ♿︎ ) WHEELCHAIR SYMBOL +267F FE0F ; emoji style; # (4.1 ♿️ ) WHEELCHAIR SYMBOL +2692 FE0E ; text style; # (4.1 ⚒︎ ) HAMMER AND PICK +2692 FE0F ; emoji style; # (4.1 ⚒️ ) HAMMER AND PICK +2693 FE0E ; text style; # (4.1 ⚓︎ ) ANCHOR +2693 FE0F ; emoji style; # (4.1 ⚓️ ) ANCHOR +2694 FE0E ; text style; # (4.1 ⚔︎ ) CROSSED SWORDS +2694 FE0F ; emoji style; # (4.1 ⚔️ ) CROSSED SWORDS +2695 FE0E ; text style; # (4.1 ⚕︎ ) STAFF OF AESCULAPIUS +2695 FE0F ; emoji style; # (4.1 ⚕️ ) STAFF OF AESCULAPIUS +2696 FE0E ; text style; # (4.1 ⚖︎ ) SCALES +2696 FE0F ; emoji style; # (4.1 ⚖️ ) SCALES +2697 FE0E ; text style; # (4.1 ⚗︎ ) ALEMBIC +2697 FE0F ; emoji style; # (4.1 ⚗️ ) ALEMBIC +2699 FE0E ; text style; # (4.1 ⚙︎ ) GEAR +2699 FE0F ; emoji style; # (4.1 ⚙️ ) GEAR +269B FE0E ; text style; # (4.1 ⚛︎ ) ATOM SYMBOL +269B FE0F ; emoji style; # (4.1 ⚛️ ) ATOM SYMBOL +269C FE0E ; text style; # (4.1 ⚜︎ ) FLEUR-DE-LIS +269C FE0F ; emoji style; # (4.1 ⚜️ ) FLEUR-DE-LIS +26A0 FE0E ; text style; # (4.0 ⚠︎ ) WARNING SIGN +26A0 FE0F ; emoji style; # (4.0 ⚠️ ) WARNING SIGN +26A1 FE0E ; text style; # (4.0 ⚡︎ ) HIGH VOLTAGE SIGN +26A1 FE0F ; emoji style; # (4.0 ⚡️ ) HIGH VOLTAGE SIGN +26A7 FE0E ; text style; # (4.1 ⚧︎ ) MALE WITH STROKE AND MALE AND FEMALE SIGN +26A7 FE0F ; emoji style; # (4.1 ⚧️ ) MALE WITH STROKE AND MALE AND FEMALE SIGN +26AA FE0E ; text style; # (4.1 ⚪︎ ) MEDIUM WHITE CIRCLE +26AA FE0F ; emoji style; # (4.1 ⚪️ ) MEDIUM WHITE CIRCLE +26AB FE0E ; text style; # (4.1 ⚫︎ ) MEDIUM BLACK CIRCLE +26AB FE0F ; emoji style; # (4.1 ⚫️ ) MEDIUM BLACK CIRCLE +26B0 FE0E ; text style; # (4.1 ⚰︎ ) COFFIN +26B0 FE0F ; emoji style; # (4.1 ⚰️ ) COFFIN +26B1 FE0E ; text style; # (4.1 ⚱︎ ) FUNERAL URN +26B1 FE0F ; emoji style; # (4.1 ⚱️ ) FUNERAL URN +26BD FE0E ; text style; # (5.2 ⚽︎ ) SOCCER BALL +26BD FE0F ; emoji style; # (5.2 ⚽️ ) SOCCER BALL +26BE FE0E ; text style; # (5.2 ⚾︎ ) BASEBALL +26BE FE0F ; emoji style; # (5.2 ⚾️ ) BASEBALL +26C4 FE0E ; text style; # (5.2 ⛄︎ ) SNOWMAN WITHOUT SNOW +26C4 FE0F ; emoji style; # (5.2 ⛄️ ) SNOWMAN WITHOUT SNOW +26C5 FE0E ; text style; # (5.2 ⛅︎ ) SUN BEHIND CLOUD +26C5 FE0F ; emoji style; # (5.2 ⛅️ ) SUN BEHIND CLOUD +26C8 FE0E ; text style; # (5.2 ⛈︎ ) THUNDER CLOUD AND RAIN +26C8 FE0F ; emoji style; # (5.2 ⛈️ ) THUNDER CLOUD AND RAIN +26CF FE0E ; text style; # (5.2 ⛏︎ ) PICK +26CF FE0F ; emoji style; # (5.2 ⛏️ ) PICK +26D1 FE0E ; text style; # (5.2 ⛑︎ ) HELMET WITH WHITE CROSS +26D1 FE0F ; emoji style; # (5.2 ⛑️ ) HELMET WITH WHITE CROSS +26D3 FE0E ; text style; # (5.2 ⛓︎ ) CHAINS +26D3 FE0F ; emoji style; # (5.2 ⛓️ ) CHAINS +26D4 FE0E ; text style; # (5.2 ⛔︎ ) NO ENTRY +26D4 FE0F ; emoji style; # (5.2 ⛔️ ) NO ENTRY +26E9 FE0E ; text style; # (5.2 ⛩︎ ) SHINTO SHRINE +26E9 FE0F ; emoji style; # (5.2 ⛩️ ) SHINTO SHRINE +26EA FE0E ; text style; # (5.2 ⛪︎ ) CHURCH +26EA FE0F ; emoji style; # (5.2 ⛪️ ) CHURCH +26F0 FE0E ; text style; # (5.2 ⛰︎ ) MOUNTAIN +26F0 FE0F ; emoji style; # (5.2 ⛰️ ) MOUNTAIN +26F1 FE0E ; text style; # (5.2 ⛱︎ ) UMBRELLA ON GROUND +26F1 FE0F ; emoji style; # (5.2 ⛱️ ) UMBRELLA ON GROUND +26F2 FE0E ; text style; # (5.2 ⛲︎ ) FOUNTAIN +26F2 FE0F ; emoji style; # (5.2 ⛲️ ) FOUNTAIN +26F3 FE0E ; text style; # (5.2 ⛳︎ ) FLAG IN HOLE +26F3 FE0F ; emoji style; # (5.2 ⛳️ ) FLAG IN HOLE +26F4 FE0E ; text style; # (5.2 ⛴︎ ) FERRY +26F4 FE0F ; emoji style; # (5.2 ⛴️ ) FERRY +26F5 FE0E ; text style; # (5.2 ⛵︎ ) SAILBOAT +26F5 FE0F ; emoji style; # (5.2 ⛵️ ) SAILBOAT +26F7 FE0E ; text style; # (5.2 ⛷︎ ) SKIER +26F7 FE0F ; emoji style; # (5.2 ⛷️ ) SKIER +26F8 FE0E ; text style; # (5.2 ⛸︎ ) ICE SKATE +26F8 FE0F ; emoji style; # (5.2 ⛸️ ) ICE SKATE +26F9 FE0E ; text style; # (5.2 ⛹︎ ) PERSON WITH BALL +26F9 FE0F ; emoji style; # (5.2 ⛹️ ) PERSON WITH BALL +26FA FE0E ; text style; # (5.2 ⛺︎ ) TENT +26FA FE0F ; emoji style; # (5.2 ⛺️ ) TENT +26FD FE0E ; text style; # (5.2 ⛽︎ ) FUEL PUMP +26FD FE0F ; emoji style; # (5.2 ⛽️ ) FUEL PUMP +2702 FE0E ; text style; # (1.1 ✂︎ ) BLACK SCISSORS +2702 FE0F ; emoji style; # (1.1 ✂️ ) BLACK SCISSORS +2708 FE0E ; text style; # (1.1 ✈︎ ) AIRPLANE +2708 FE0F ; emoji style; # (1.1 ✈️ ) AIRPLANE +2709 FE0E ; text style; # (1.1 ✉︎ ) ENVELOPE +2709 FE0F ; emoji style; # (1.1 ✉️ ) ENVELOPE +270C FE0E ; text style; # (1.1 ✌︎ ) VICTORY HAND +270C FE0F ; emoji style; # (1.1 ✌️ ) VICTORY HAND +270D FE0E ; text style; # (1.1 ✍︎ ) WRITING HAND +270D FE0F ; emoji style; # (1.1 ✍️ ) WRITING HAND +270F FE0E ; text style; # (1.1 ✏︎ ) PENCIL +270F FE0F ; emoji style; # (1.1 ✏️ ) PENCIL +2712 FE0E ; text style; # (1.1 ✒︎ ) BLACK NIB +2712 FE0F ; emoji style; # (1.1 ✒️ ) BLACK NIB +2714 FE0E ; text style; # (1.1 ✔︎ ) HEAVY CHECK MARK +2714 FE0F ; emoji style; # (1.1 ✔️ ) HEAVY CHECK MARK +2716 FE0E ; text style; # (1.1 ✖︎ ) HEAVY MULTIPLICATION X +2716 FE0F ; emoji style; # (1.1 ✖️ ) HEAVY MULTIPLICATION X +271D FE0E ; text style; # (1.1 ✝︎ ) LATIN CROSS +271D FE0F ; emoji style; # (1.1 ✝️ ) LATIN CROSS +2721 FE0E ; text style; # (1.1 ✡︎ ) STAR OF DAVID +2721 FE0F ; emoji style; # (1.1 ✡️ ) STAR OF DAVID +2733 FE0E ; text style; # (1.1 ✳︎ ) EIGHT SPOKED ASTERISK +2733 FE0F ; emoji style; # (1.1 ✳️ ) EIGHT SPOKED ASTERISK +2734 FE0E ; text style; # (1.1 ✴︎ ) EIGHT POINTED BLACK STAR +2734 FE0F ; emoji style; # (1.1 ✴️ ) EIGHT POINTED BLACK STAR +2744 FE0E ; text style; # (1.1 ❄︎ ) SNOWFLAKE +2744 FE0F ; emoji style; # (1.1 ❄️ ) SNOWFLAKE +2747 FE0E ; text style; # (1.1 ❇︎ ) SPARKLE +2747 FE0F ; emoji style; # (1.1 ❇️ ) SPARKLE +2753 FE0E ; text style; # (6.0 ❓︎ ) BLACK QUESTION MARK ORNAMENT +2753 FE0F ; emoji style; # (6.0 ❓️ ) BLACK QUESTION MARK ORNAMENT +2757 FE0E ; text style; # (5.2 ❗︎ ) HEAVY EXCLAMATION MARK SYMBOL +2757 FE0F ; emoji style; # (5.2 ❗️ ) HEAVY EXCLAMATION MARK SYMBOL +2763 FE0E ; text style; # (1.1 ❣︎ ) HEAVY HEART EXCLAMATION MARK ORNAMENT +2763 FE0F ; emoji style; # (1.1 ❣️ ) HEAVY HEART EXCLAMATION MARK ORNAMENT +2764 FE0E ; text style; # (1.1 ❤︎ ) HEAVY BLACK HEART +2764 FE0F ; emoji style; # (1.1 ❤️ ) HEAVY BLACK HEART +27A1 FE0E ; text style; # (1.1 ➡︎ ) BLACK RIGHTWARDS ARROW +27A1 FE0F ; emoji style; # (1.1 ➡️ ) BLACK RIGHTWARDS ARROW +2934 FE0E ; text style; # (3.2 ⤴︎ ) ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS +2934 FE0F ; emoji style; # (3.2 ⤴️ ) ARROW POINTING RIGHTWARDS THEN CURVING UPWARDS +2935 FE0E ; text style; # (3.2 ⤵︎ ) ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS +2935 FE0F ; emoji style; # (3.2 ⤵️ ) ARROW POINTING RIGHTWARDS THEN CURVING DOWNWARDS +2B05 FE0E ; text style; # (4.0 ⬅︎ ) LEFTWARDS BLACK ARROW +2B05 FE0F ; emoji style; # (4.0 ⬅️ ) LEFTWARDS BLACK ARROW +2B06 FE0E ; text style; # (4.0 ⬆︎ ) UPWARDS BLACK ARROW +2B06 FE0F ; emoji style; # (4.0 ⬆️ ) UPWARDS BLACK ARROW +2B07 FE0E ; text style; # (4.0 ⬇︎ ) DOWNWARDS BLACK ARROW +2B07 FE0F ; emoji style; # (4.0 ⬇️ ) DOWNWARDS BLACK ARROW +2B1B FE0E ; text style; # (5.1 ⬛︎ ) BLACK LARGE SQUARE +2B1B FE0F ; emoji style; # (5.1 ⬛️ ) BLACK LARGE SQUARE +2B1C FE0E ; text style; # (5.1 ⬜︎ ) WHITE LARGE SQUARE +2B1C FE0F ; emoji style; # (5.1 ⬜️ ) WHITE LARGE SQUARE +2B50 FE0E ; text style; # (5.1 ⭐︎ ) WHITE MEDIUM STAR +2B50 FE0F ; emoji style; # (5.1 ⭐️ ) WHITE MEDIUM STAR +2B55 FE0E ; text style; # (5.2 ⭕︎ ) HEAVY LARGE CIRCLE +2B55 FE0F ; emoji style; # (5.2 ⭕️ ) HEAVY LARGE CIRCLE +3030 FE0E ; text style; # (1.1 〰︎ ) WAVY DASH +3030 FE0F ; emoji style; # (1.1 〰️ ) WAVY DASH +303D FE0E ; text style; # (3.2 〽︎ ) PART ALTERNATION MARK +303D FE0F ; emoji style; # (3.2 〽️ ) PART ALTERNATION MARK +3297 FE0E ; text style; # (1.1 ㊗︎ ) CIRCLED IDEOGRAPH CONGRATULATION +3297 FE0F ; emoji style; # (1.1 ㊗️ ) CIRCLED IDEOGRAPH CONGRATULATION +3299 FE0E ; text style; # (1.1 ㊙︎ ) CIRCLED IDEOGRAPH SECRET +3299 FE0F ; emoji style; # (1.1 ㊙️ ) CIRCLED IDEOGRAPH SECRET +1F004 FE0E ; text style; # (5.1 🀄︎ ) MAHJONG TILE RED DRAGON +1F004 FE0F ; emoji style; # (5.1 🀄️ ) MAHJONG TILE RED DRAGON +1F170 FE0E ; text style; # (6.0 🅰︎ ) NEGATIVE SQUARED LATIN CAPITAL LETTER A +1F170 FE0F ; emoji style; # (6.0 🅰️ ) NEGATIVE SQUARED LATIN CAPITAL LETTER A +1F171 FE0E ; text style; # (6.0 🅱︎ ) NEGATIVE SQUARED LATIN CAPITAL LETTER B +1F171 FE0F ; emoji style; # (6.0 🅱️ ) NEGATIVE SQUARED LATIN CAPITAL LETTER B +1F17E FE0E ; text style; # (6.0 🅾︎ ) NEGATIVE SQUARED LATIN CAPITAL LETTER O +1F17E FE0F ; emoji style; # (6.0 🅾️ ) NEGATIVE SQUARED LATIN CAPITAL LETTER O +1F17F FE0E ; text style; # (5.2 🅿︎ ) NEGATIVE SQUARED LATIN CAPITAL LETTER P +1F17F FE0F ; emoji style; # (5.2 🅿️ ) NEGATIVE SQUARED LATIN CAPITAL LETTER P +1F202 FE0E ; text style; # (6.0 🈂︎ ) SQUARED KATAKANA SA +1F202 FE0F ; emoji style; # (6.0 🈂️ ) SQUARED KATAKANA SA +1F21A FE0E ; text style; # (5.2 🈚︎ ) SQUARED CJK UNIFIED IDEOGRAPH-7121 +1F21A FE0F ; emoji style; # (5.2 🈚️ ) SQUARED CJK UNIFIED IDEOGRAPH-7121 +1F22F FE0E ; text style; # (5.2 🈯︎ ) SQUARED CJK UNIFIED IDEOGRAPH-6307 +1F22F FE0F ; emoji style; # (5.2 🈯️ ) SQUARED CJK UNIFIED IDEOGRAPH-6307 +1F237 FE0E ; text style; # (6.0 🈷︎ ) SQUARED CJK UNIFIED IDEOGRAPH-6708 +1F237 FE0F ; emoji style; # (6.0 🈷️ ) SQUARED CJK UNIFIED IDEOGRAPH-6708 +1F30D FE0E ; text style; # (6.0 🌍︎ ) EARTH GLOBE EUROPE-AFRICA +1F30D FE0F ; emoji style; # (6.0 🌍️ ) EARTH GLOBE EUROPE-AFRICA +1F30E FE0E ; text style; # (6.0 🌎︎ ) EARTH GLOBE AMERICAS +1F30E FE0F ; emoji style; # (6.0 🌎️ ) EARTH GLOBE AMERICAS +1F30F FE0E ; text style; # (6.0 🌏︎ ) EARTH GLOBE ASIA-AUSTRALIA +1F30F FE0F ; emoji style; # (6.0 🌏️ ) EARTH GLOBE ASIA-AUSTRALIA +1F315 FE0E ; text style; # (6.0 🌕︎ ) FULL MOON SYMBOL +1F315 FE0F ; emoji style; # (6.0 🌕️ ) FULL MOON SYMBOL +1F31C FE0E ; text style; # (6.0 🌜︎ ) LAST QUARTER MOON WITH FACE +1F31C FE0F ; emoji style; # (6.0 🌜️ ) LAST QUARTER MOON WITH FACE +1F321 FE0E ; text style; # (7.0 🌡︎ ) THERMOMETER +1F321 FE0F ; emoji style; # (7.0 🌡️ ) THERMOMETER +1F324 FE0E ; text style; # (7.0 🌤︎ ) WHITE SUN WITH SMALL CLOUD +1F324 FE0F ; emoji style; # (7.0 🌤️ ) WHITE SUN WITH SMALL CLOUD +1F325 FE0E ; text style; # (7.0 🌥︎ ) WHITE SUN BEHIND CLOUD +1F325 FE0F ; emoji style; # (7.0 🌥️ ) WHITE SUN BEHIND CLOUD +1F326 FE0E ; text style; # (7.0 🌦︎ ) WHITE SUN BEHIND CLOUD WITH RAIN +1F326 FE0F ; emoji style; # (7.0 🌦️ ) WHITE SUN BEHIND CLOUD WITH RAIN +1F327 FE0E ; text style; # (7.0 🌧︎ ) CLOUD WITH RAIN +1F327 FE0F ; emoji style; # (7.0 🌧️ ) CLOUD WITH RAIN +1F328 FE0E ; text style; # (7.0 🌨︎ ) CLOUD WITH SNOW +1F328 FE0F ; emoji style; # (7.0 🌨️ ) CLOUD WITH SNOW +1F329 FE0E ; text style; # (7.0 🌩︎ ) CLOUD WITH LIGHTNING +1F329 FE0F ; emoji style; # (7.0 🌩️ ) CLOUD WITH LIGHTNING +1F32A FE0E ; text style; # (7.0 🌪︎ ) CLOUD WITH TORNADO +1F32A FE0F ; emoji style; # (7.0 🌪️ ) CLOUD WITH TORNADO +1F32B FE0E ; text style; # (7.0 🌫︎ ) FOG +1F32B FE0F ; emoji style; # (7.0 🌫️ ) FOG +1F32C FE0E ; text style; # (7.0 🌬︎ ) WIND BLOWING FACE +1F32C FE0F ; emoji style; # (7.0 🌬️ ) WIND BLOWING FACE +1F336 FE0E ; text style; # (7.0 🌶︎ ) HOT PEPPER +1F336 FE0F ; emoji style; # (7.0 🌶️ ) HOT PEPPER +1F378 FE0E ; text style; # (6.0 🍸︎ ) COCKTAIL GLASS +1F378 FE0F ; emoji style; # (6.0 🍸️ ) COCKTAIL GLASS +1F37D FE0E ; text style; # (7.0 🍽︎ ) FORK AND KNIFE WITH PLATE +1F37D FE0F ; emoji style; # (7.0 🍽️ ) FORK AND KNIFE WITH PLATE +1F393 FE0E ; text style; # (6.0 🎓︎ ) GRADUATION CAP +1F393 FE0F ; emoji style; # (6.0 🎓️ ) GRADUATION CAP +1F396 FE0E ; text style; # (7.0 🎖︎ ) MILITARY MEDAL +1F396 FE0F ; emoji style; # (7.0 🎖️ ) MILITARY MEDAL +1F397 FE0E ; text style; # (7.0 🎗︎ ) REMINDER RIBBON +1F397 FE0F ; emoji style; # (7.0 🎗️ ) REMINDER RIBBON +1F399 FE0E ; text style; # (7.0 🎙︎ ) STUDIO MICROPHONE +1F399 FE0F ; emoji style; # (7.0 🎙️ ) STUDIO MICROPHONE +1F39A FE0E ; text style; # (7.0 🎚︎ ) LEVEL SLIDER +1F39A FE0F ; emoji style; # (7.0 🎚️ ) LEVEL SLIDER +1F39B FE0E ; text style; # (7.0 🎛︎ ) CONTROL KNOBS +1F39B FE0F ; emoji style; # (7.0 🎛️ ) CONTROL KNOBS +1F39E FE0E ; text style; # (7.0 🎞︎ ) FILM FRAMES +1F39E FE0F ; emoji style; # (7.0 🎞️ ) FILM FRAMES +1F39F FE0E ; text style; # (7.0 🎟︎ ) ADMISSION TICKETS +1F39F FE0F ; emoji style; # (7.0 🎟️ ) ADMISSION TICKETS +1F3A7 FE0E ; text style; # (6.0 🎧︎ ) HEADPHONE +1F3A7 FE0F ; emoji style; # (6.0 🎧️ ) HEADPHONE +1F3AC FE0E ; text style; # (6.0 🎬︎ ) CLAPPER BOARD +1F3AC FE0F ; emoji style; # (6.0 🎬️ ) CLAPPER BOARD +1F3AD FE0E ; text style; # (6.0 🎭︎ ) PERFORMING ARTS +1F3AD FE0F ; emoji style; # (6.0 🎭️ ) PERFORMING ARTS +1F3AE FE0E ; text style; # (6.0 🎮︎ ) VIDEO GAME +1F3AE FE0F ; emoji style; # (6.0 🎮️ ) VIDEO GAME +1F3C2 FE0E ; text style; # (6.0 🏂︎ ) SNOWBOARDER +1F3C2 FE0F ; emoji style; # (6.0 🏂️ ) SNOWBOARDER +1F3C4 FE0E ; text style; # (6.0 🏄︎ ) SURFER +1F3C4 FE0F ; emoji style; # (6.0 🏄️ ) SURFER +1F3C6 FE0E ; text style; # (6.0 🏆︎ ) TROPHY +1F3C6 FE0F ; emoji style; # (6.0 🏆️ ) TROPHY +1F3CA FE0E ; text style; # (6.0 🏊︎ ) SWIMMER +1F3CA FE0F ; emoji style; # (6.0 🏊️ ) SWIMMER +1F3CB FE0E ; text style; # (7.0 🏋︎ ) WEIGHT LIFTER +1F3CB FE0F ; emoji style; # (7.0 🏋️ ) WEIGHT LIFTER +1F3CC FE0E ; text style; # (7.0 🏌︎ ) GOLFER +1F3CC FE0F ; emoji style; # (7.0 🏌️ ) GOLFER +1F3CD FE0E ; text style; # (7.0 🏍︎ ) RACING MOTORCYCLE +1F3CD FE0F ; emoji style; # (7.0 🏍️ ) RACING MOTORCYCLE +1F3CE FE0E ; text style; # (7.0 🏎︎ ) RACING CAR +1F3CE FE0F ; emoji style; # (7.0 🏎️ ) RACING CAR +1F3D4 FE0E ; text style; # (7.0 🏔︎ ) SNOW CAPPED MOUNTAIN +1F3D4 FE0F ; emoji style; # (7.0 🏔️ ) SNOW CAPPED MOUNTAIN +1F3D5 FE0E ; text style; # (7.0 🏕︎ ) CAMPING +1F3D5 FE0F ; emoji style; # (7.0 🏕️ ) CAMPING +1F3D6 FE0E ; text style; # (7.0 🏖︎ ) BEACH WITH UMBRELLA +1F3D6 FE0F ; emoji style; # (7.0 🏖️ ) BEACH WITH UMBRELLA +1F3D7 FE0E ; text style; # (7.0 🏗︎ ) BUILDING CONSTRUCTION +1F3D7 FE0F ; emoji style; # (7.0 🏗️ ) BUILDING CONSTRUCTION +1F3D8 FE0E ; text style; # (7.0 🏘︎ ) HOUSE BUILDINGS +1F3D8 FE0F ; emoji style; # (7.0 🏘️ ) HOUSE BUILDINGS +1F3D9 FE0E ; text style; # (7.0 🏙︎ ) CITYSCAPE +1F3D9 FE0F ; emoji style; # (7.0 🏙️ ) CITYSCAPE +1F3DA FE0E ; text style; # (7.0 🏚︎ ) DERELICT HOUSE BUILDING +1F3DA FE0F ; emoji style; # (7.0 🏚️ ) DERELICT HOUSE BUILDING +1F3DB FE0E ; text style; # (7.0 🏛︎ ) CLASSICAL BUILDING +1F3DB FE0F ; emoji style; # (7.0 🏛️ ) CLASSICAL BUILDING +1F3DC FE0E ; text style; # (7.0 🏜︎ ) DESERT +1F3DC FE0F ; emoji style; # (7.0 🏜️ ) DESERT +1F3DD FE0E ; text style; # (7.0 🏝︎ ) DESERT ISLAND +1F3DD FE0F ; emoji style; # (7.0 🏝️ ) DESERT ISLAND +1F3DE FE0E ; text style; # (7.0 🏞︎ ) NATIONAL PARK +1F3DE FE0F ; emoji style; # (7.0 🏞️ ) NATIONAL PARK +1F3DF FE0E ; text style; # (7.0 🏟︎ ) STADIUM +1F3DF FE0F ; emoji style; # (7.0 🏟️ ) STADIUM +1F3E0 FE0E ; text style; # (6.0 🏠︎ ) HOUSE BUILDING +1F3E0 FE0F ; emoji style; # (6.0 🏠️ ) HOUSE BUILDING +1F3ED FE0E ; text style; # (6.0 🏭︎ ) FACTORY +1F3ED FE0F ; emoji style; # (6.0 🏭️ ) FACTORY +1F3F3 FE0E ; text style; # (7.0 🏳︎ ) WAVING WHITE FLAG +1F3F3 FE0F ; emoji style; # (7.0 🏳️ ) WAVING WHITE FLAG +1F3F5 FE0E ; text style; # (7.0 🏵︎ ) ROSETTE +1F3F5 FE0F ; emoji style; # (7.0 🏵️ ) ROSETTE +1F3F7 FE0E ; text style; # (7.0 🏷︎ ) LABEL +1F3F7 FE0F ; emoji style; # (7.0 🏷️ ) LABEL +1F408 FE0E ; text style; # (6.0 🐈︎ ) CAT +1F408 FE0F ; emoji style; # (6.0 🐈️ ) CAT +1F415 FE0E ; text style; # (6.0 🐕︎ ) DOG +1F415 FE0F ; emoji style; # (6.0 🐕️ ) DOG +1F41F FE0E ; text style; # (6.0 🐟︎ ) FISH +1F41F FE0F ; emoji style; # (6.0 🐟️ ) FISH +1F426 FE0E ; text style; # (6.0 🐦︎ ) BIRD +1F426 FE0F ; emoji style; # (6.0 🐦️ ) BIRD +1F43F FE0E ; text style; # (7.0 🐿︎ ) CHIPMUNK +1F43F FE0F ; emoji style; # (7.0 🐿️ ) CHIPMUNK +1F441 FE0E ; text style; # (7.0 👁︎ ) EYE +1F441 FE0F ; emoji style; # (7.0 👁️ ) EYE +1F442 FE0E ; text style; # (6.0 👂︎ ) EAR +1F442 FE0F ; emoji style; # (6.0 👂️ ) EAR +1F446 FE0E ; text style; # (6.0 👆︎ ) WHITE UP POINTING BACKHAND INDEX +1F446 FE0F ; emoji style; # (6.0 👆️ ) WHITE UP POINTING BACKHAND INDEX +1F447 FE0E ; text style; # (6.0 👇︎ ) WHITE DOWN POINTING BACKHAND INDEX +1F447 FE0F ; emoji style; # (6.0 👇️ ) WHITE DOWN POINTING BACKHAND INDEX +1F448 FE0E ; text style; # (6.0 👈︎ ) WHITE LEFT POINTING BACKHAND INDEX +1F448 FE0F ; emoji style; # (6.0 👈️ ) WHITE LEFT POINTING BACKHAND INDEX +1F449 FE0E ; text style; # (6.0 👉︎ ) WHITE RIGHT POINTING BACKHAND INDEX +1F449 FE0F ; emoji style; # (6.0 👉️ ) WHITE RIGHT POINTING BACKHAND INDEX +1F44D FE0E ; text style; # (6.0 👍︎ ) THUMBS UP SIGN +1F44D FE0F ; emoji style; # (6.0 👍️ ) THUMBS UP SIGN +1F44E FE0E ; text style; # (6.0 👎︎ ) THUMBS DOWN SIGN +1F44E FE0F ; emoji style; # (6.0 👎️ ) THUMBS DOWN SIGN +1F453 FE0E ; text style; # (6.0 👓︎ ) EYEGLASSES +1F453 FE0F ; emoji style; # (6.0 👓️ ) EYEGLASSES +1F46A FE0E ; text style; # (6.0 👪︎ ) FAMILY +1F46A FE0F ; emoji style; # (6.0 👪️ ) FAMILY +1F47D FE0E ; text style; # (6.0 👽︎ ) EXTRATERRESTRIAL ALIEN +1F47D FE0F ; emoji style; # (6.0 👽️ ) EXTRATERRESTRIAL ALIEN +1F4A3 FE0E ; text style; # (6.0 💣︎ ) BOMB +1F4A3 FE0F ; emoji style; # (6.0 💣️ ) BOMB +1F4B0 FE0E ; text style; # (6.0 💰︎ ) MONEY BAG +1F4B0 FE0F ; emoji style; # (6.0 💰️ ) MONEY BAG +1F4B3 FE0E ; text style; # (6.0 💳︎ ) CREDIT CARD +1F4B3 FE0F ; emoji style; # (6.0 💳️ ) CREDIT CARD +1F4BB FE0E ; text style; # (6.0 💻︎ ) PERSONAL COMPUTER +1F4BB FE0F ; emoji style; # (6.0 💻️ ) PERSONAL COMPUTER +1F4BF FE0E ; text style; # (6.0 💿︎ ) OPTICAL DISC +1F4BF FE0F ; emoji style; # (6.0 💿️ ) OPTICAL DISC +1F4CB FE0E ; text style; # (6.0 📋︎ ) CLIPBOARD +1F4CB FE0F ; emoji style; # (6.0 📋️ ) CLIPBOARD +1F4DA FE0E ; text style; # (6.0 📚︎ ) BOOKS +1F4DA FE0F ; emoji style; # (6.0 📚️ ) BOOKS +1F4DF FE0E ; text style; # (6.0 📟︎ ) PAGER +1F4DF FE0F ; emoji style; # (6.0 📟️ ) PAGER +1F4E4 FE0E ; text style; # (6.0 📤︎ ) OUTBOX TRAY +1F4E4 FE0F ; emoji style; # (6.0 📤️ ) OUTBOX TRAY +1F4E5 FE0E ; text style; # (6.0 📥︎ ) INBOX TRAY +1F4E5 FE0F ; emoji style; # (6.0 📥️ ) INBOX TRAY +1F4E6 FE0E ; text style; # (6.0 📦︎ ) PACKAGE +1F4E6 FE0F ; emoji style; # (6.0 📦️ ) PACKAGE +1F4EA FE0E ; text style; # (6.0 📪︎ ) CLOSED MAILBOX WITH LOWERED FLAG +1F4EA FE0F ; emoji style; # (6.0 📪️ ) CLOSED MAILBOX WITH LOWERED FLAG +1F4EB FE0E ; text style; # (6.0 📫︎ ) CLOSED MAILBOX WITH RAISED FLAG +1F4EB FE0F ; emoji style; # (6.0 📫️ ) CLOSED MAILBOX WITH RAISED FLAG +1F4EC FE0E ; text style; # (6.0 📬︎ ) OPEN MAILBOX WITH RAISED FLAG +1F4EC FE0F ; emoji style; # (6.0 📬️ ) OPEN MAILBOX WITH RAISED FLAG +1F4ED FE0E ; text style; # (6.0 📭︎ ) OPEN MAILBOX WITH LOWERED FLAG +1F4ED FE0F ; emoji style; # (6.0 📭️ ) OPEN MAILBOX WITH LOWERED FLAG +1F4F7 FE0E ; text style; # (6.0 📷︎ ) CAMERA +1F4F7 FE0F ; emoji style; # (6.0 📷️ ) CAMERA +1F4F9 FE0E ; text style; # (6.0 📹︎ ) VIDEO CAMERA +1F4F9 FE0F ; emoji style; # (6.0 📹️ ) VIDEO CAMERA +1F4FA FE0E ; text style; # (6.0 📺︎ ) TELEVISION +1F4FA FE0F ; emoji style; # (6.0 📺️ ) TELEVISION +1F4FB FE0E ; text style; # (6.0 📻︎ ) RADIO +1F4FB FE0F ; emoji style; # (6.0 📻️ ) RADIO +1F4FD FE0E ; text style; # (7.0 📽︎ ) FILM PROJECTOR +1F4FD FE0F ; emoji style; # (7.0 📽️ ) FILM PROJECTOR +1F508 FE0E ; text style; # (6.0 🔈︎ ) SPEAKER +1F508 FE0F ; emoji style; # (6.0 🔈️ ) SPEAKER +1F50D FE0E ; text style; # (6.0 🔍︎ ) LEFT-POINTING MAGNIFYING GLASS +1F50D FE0F ; emoji style; # (6.0 🔍️ ) LEFT-POINTING MAGNIFYING GLASS +1F512 FE0E ; text style; # (6.0 🔒︎ ) LOCK +1F512 FE0F ; emoji style; # (6.0 🔒️ ) LOCK +1F513 FE0E ; text style; # (6.0 🔓︎ ) OPEN LOCK +1F513 FE0F ; emoji style; # (6.0 🔓️ ) OPEN LOCK +1F549 FE0E ; text style; # (7.0 🕉︎ ) OM SYMBOL +1F549 FE0F ; emoji style; # (7.0 🕉️ ) OM SYMBOL +1F54A FE0E ; text style; # (7.0 🕊︎ ) DOVE OF PEACE +1F54A FE0F ; emoji style; # (7.0 🕊️ ) DOVE OF PEACE +1F550 FE0E ; text style; # (6.0 🕐︎ ) CLOCK FACE ONE OCLOCK +1F550 FE0F ; emoji style; # (6.0 🕐️ ) CLOCK FACE ONE OCLOCK +1F551 FE0E ; text style; # (6.0 🕑︎ ) CLOCK FACE TWO OCLOCK +1F551 FE0F ; emoji style; # (6.0 🕑️ ) CLOCK FACE TWO OCLOCK +1F552 FE0E ; text style; # (6.0 🕒︎ ) CLOCK FACE THREE OCLOCK +1F552 FE0F ; emoji style; # (6.0 🕒️ ) CLOCK FACE THREE OCLOCK +1F553 FE0E ; text style; # (6.0 🕓︎ ) CLOCK FACE FOUR OCLOCK +1F553 FE0F ; emoji style; # (6.0 🕓️ ) CLOCK FACE FOUR OCLOCK +1F554 FE0E ; text style; # (6.0 🕔︎ ) CLOCK FACE FIVE OCLOCK +1F554 FE0F ; emoji style; # (6.0 🕔️ ) CLOCK FACE FIVE OCLOCK +1F555 FE0E ; text style; # (6.0 🕕︎ ) CLOCK FACE SIX OCLOCK +1F555 FE0F ; emoji style; # (6.0 🕕️ ) CLOCK FACE SIX OCLOCK +1F556 FE0E ; text style; # (6.0 🕖︎ ) CLOCK FACE SEVEN OCLOCK +1F556 FE0F ; emoji style; # (6.0 🕖️ ) CLOCK FACE SEVEN OCLOCK +1F557 FE0E ; text style; # (6.0 🕗︎ ) CLOCK FACE EIGHT OCLOCK +1F557 FE0F ; emoji style; # (6.0 🕗️ ) CLOCK FACE EIGHT OCLOCK +1F558 FE0E ; text style; # (6.0 🕘︎ ) CLOCK FACE NINE OCLOCK +1F558 FE0F ; emoji style; # (6.0 🕘️ ) CLOCK FACE NINE OCLOCK +1F559 FE0E ; text style; # (6.0 🕙︎ ) CLOCK FACE TEN OCLOCK +1F559 FE0F ; emoji style; # (6.0 🕙️ ) CLOCK FACE TEN OCLOCK +1F55A FE0E ; text style; # (6.0 🕚︎ ) CLOCK FACE ELEVEN OCLOCK +1F55A FE0F ; emoji style; # (6.0 🕚️ ) CLOCK FACE ELEVEN OCLOCK +1F55B FE0E ; text style; # (6.0 🕛︎ ) CLOCK FACE TWELVE OCLOCK +1F55B FE0F ; emoji style; # (6.0 🕛️ ) CLOCK FACE TWELVE OCLOCK +1F55C FE0E ; text style; # (6.0 🕜︎ ) CLOCK FACE ONE-THIRTY +1F55C FE0F ; emoji style; # (6.0 🕜️ ) CLOCK FACE ONE-THIRTY +1F55D FE0E ; text style; # (6.0 🕝︎ ) CLOCK FACE TWO-THIRTY +1F55D FE0F ; emoji style; # (6.0 🕝️ ) CLOCK FACE TWO-THIRTY +1F55E FE0E ; text style; # (6.0 🕞︎ ) CLOCK FACE THREE-THIRTY +1F55E FE0F ; emoji style; # (6.0 🕞️ ) CLOCK FACE THREE-THIRTY +1F55F FE0E ; text style; # (6.0 🕟︎ ) CLOCK FACE FOUR-THIRTY +1F55F FE0F ; emoji style; # (6.0 🕟️ ) CLOCK FACE FOUR-THIRTY +1F560 FE0E ; text style; # (6.0 🕠︎ ) CLOCK FACE FIVE-THIRTY +1F560 FE0F ; emoji style; # (6.0 🕠️ ) CLOCK FACE FIVE-THIRTY +1F561 FE0E ; text style; # (6.0 🕡︎ ) CLOCK FACE SIX-THIRTY +1F561 FE0F ; emoji style; # (6.0 🕡️ ) CLOCK FACE SIX-THIRTY +1F562 FE0E ; text style; # (6.0 🕢︎ ) CLOCK FACE SEVEN-THIRTY +1F562 FE0F ; emoji style; # (6.0 🕢️ ) CLOCK FACE SEVEN-THIRTY +1F563 FE0E ; text style; # (6.0 🕣︎ ) CLOCK FACE EIGHT-THIRTY +1F563 FE0F ; emoji style; # (6.0 🕣️ ) CLOCK FACE EIGHT-THIRTY +1F564 FE0E ; text style; # (6.0 🕤︎ ) CLOCK FACE NINE-THIRTY +1F564 FE0F ; emoji style; # (6.0 🕤️ ) CLOCK FACE NINE-THIRTY +1F565 FE0E ; text style; # (6.0 🕥︎ ) CLOCK FACE TEN-THIRTY +1F565 FE0F ; emoji style; # (6.0 🕥️ ) CLOCK FACE TEN-THIRTY +1F566 FE0E ; text style; # (6.0 🕦︎ ) CLOCK FACE ELEVEN-THIRTY +1F566 FE0F ; emoji style; # (6.0 🕦️ ) CLOCK FACE ELEVEN-THIRTY +1F567 FE0E ; text style; # (6.0 🕧︎ ) CLOCK FACE TWELVE-THIRTY +1F567 FE0F ; emoji style; # (6.0 🕧️ ) CLOCK FACE TWELVE-THIRTY +1F56F FE0E ; text style; # (7.0 🕯︎ ) CANDLE +1F56F FE0F ; emoji style; # (7.0 🕯️ ) CANDLE +1F570 FE0E ; text style; # (7.0 🕰︎ ) MANTELPIECE CLOCK +1F570 FE0F ; emoji style; # (7.0 🕰️ ) MANTELPIECE CLOCK +1F573 FE0E ; text style; # (7.0 🕳︎ ) HOLE +1F573 FE0F ; emoji style; # (7.0 🕳️ ) HOLE +1F574 FE0E ; text style; # (7.0 🕴︎ ) MAN IN BUSINESS SUIT LEVITATING +1F574 FE0F ; emoji style; # (7.0 🕴️ ) MAN IN BUSINESS SUIT LEVITATING +1F575 FE0E ; text style; # (7.0 🕵︎ ) SLEUTH OR SPY +1F575 FE0F ; emoji style; # (7.0 🕵️ ) SLEUTH OR SPY +1F576 FE0E ; text style; # (7.0 🕶︎ ) DARK SUNGLASSES +1F576 FE0F ; emoji style; # (7.0 🕶️ ) DARK SUNGLASSES +1F577 FE0E ; text style; # (7.0 🕷︎ ) SPIDER +1F577 FE0F ; emoji style; # (7.0 🕷️ ) SPIDER +1F578 FE0E ; text style; # (7.0 🕸︎ ) SPIDER WEB +1F578 FE0F ; emoji style; # (7.0 🕸️ ) SPIDER WEB +1F579 FE0E ; text style; # (7.0 🕹︎ ) JOYSTICK +1F579 FE0F ; emoji style; # (7.0 🕹️ ) JOYSTICK +1F587 FE0E ; text style; # (7.0 🖇︎ ) LINKED PAPERCLIPS +1F587 FE0F ; emoji style; # (7.0 🖇️ ) LINKED PAPERCLIPS +1F58A FE0E ; text style; # (7.0 🖊︎ ) LOWER LEFT BALLPOINT PEN +1F58A FE0F ; emoji style; # (7.0 🖊️ ) LOWER LEFT BALLPOINT PEN +1F58B FE0E ; text style; # (7.0 🖋︎ ) LOWER LEFT FOUNTAIN PEN +1F58B FE0F ; emoji style; # (7.0 🖋️ ) LOWER LEFT FOUNTAIN PEN +1F58C FE0E ; text style; # (7.0 🖌︎ ) LOWER LEFT PAINTBRUSH +1F58C FE0F ; emoji style; # (7.0 🖌️ ) LOWER LEFT PAINTBRUSH +1F58D FE0E ; text style; # (7.0 🖍︎ ) LOWER LEFT CRAYON +1F58D FE0F ; emoji style; # (7.0 🖍️ ) LOWER LEFT CRAYON +1F590 FE0E ; text style; # (7.0 🖐︎ ) RAISED HAND WITH FINGERS SPLAYED +1F590 FE0F ; emoji style; # (7.0 🖐️ ) RAISED HAND WITH FINGERS SPLAYED +1F5A5 FE0E ; text style; # (7.0 🖥︎ ) DESKTOP COMPUTER +1F5A5 FE0F ; emoji style; # (7.0 🖥️ ) DESKTOP COMPUTER +1F5A8 FE0E ; text style; # (7.0 🖨︎ ) PRINTER +1F5A8 FE0F ; emoji style; # (7.0 🖨️ ) PRINTER +1F5B1 FE0E ; text style; # (7.0 🖱︎ ) THREE BUTTON MOUSE +1F5B1 FE0F ; emoji style; # (7.0 🖱️ ) THREE BUTTON MOUSE +1F5B2 FE0E ; text style; # (7.0 🖲︎ ) TRACKBALL +1F5B2 FE0F ; emoji style; # (7.0 🖲️ ) TRACKBALL +1F5BC FE0E ; text style; # (7.0 🖼︎ ) FRAME WITH PICTURE +1F5BC FE0F ; emoji style; # (7.0 🖼️ ) FRAME WITH PICTURE +1F5C2 FE0E ; text style; # (7.0 🗂︎ ) CARD INDEX DIVIDERS +1F5C2 FE0F ; emoji style; # (7.0 🗂️ ) CARD INDEX DIVIDERS +1F5C3 FE0E ; text style; # (7.0 🗃︎ ) CARD FILE BOX +1F5C3 FE0F ; emoji style; # (7.0 🗃️ ) CARD FILE BOX +1F5C4 FE0E ; text style; # (7.0 🗄︎ ) FILE CABINET +1F5C4 FE0F ; emoji style; # (7.0 🗄️ ) FILE CABINET +1F5D1 FE0E ; text style; # (7.0 🗑︎ ) WASTEBASKET +1F5D1 FE0F ; emoji style; # (7.0 🗑️ ) WASTEBASKET +1F5D2 FE0E ; text style; # (7.0 🗒︎ ) SPIRAL NOTE PAD +1F5D2 FE0F ; emoji style; # (7.0 🗒️ ) SPIRAL NOTE PAD +1F5D3 FE0E ; text style; # (7.0 🗓︎ ) SPIRAL CALENDAR PAD +1F5D3 FE0F ; emoji style; # (7.0 🗓️ ) SPIRAL CALENDAR PAD +1F5DC FE0E ; text style; # (7.0 🗜︎ ) COMPRESSION +1F5DC FE0F ; emoji style; # (7.0 🗜️ ) COMPRESSION +1F5DD FE0E ; text style; # (7.0 🗝︎ ) OLD KEY +1F5DD FE0F ; emoji style; # (7.0 🗝️ ) OLD KEY +1F5DE FE0E ; text style; # (7.0 🗞︎ ) ROLLED-UP NEWSPAPER +1F5DE FE0F ; emoji style; # (7.0 🗞️ ) ROLLED-UP NEWSPAPER +1F5E1 FE0E ; text style; # (7.0 🗡︎ ) DAGGER KNIFE +1F5E1 FE0F ; emoji style; # (7.0 🗡️ ) DAGGER KNIFE +1F5E3 FE0E ; text style; # (7.0 🗣︎ ) SPEAKING HEAD IN SILHOUETTE +1F5E3 FE0F ; emoji style; # (7.0 🗣️ ) SPEAKING HEAD IN SILHOUETTE +1F5E8 FE0E ; text style; # (7.0 🗨︎ ) LEFT SPEECH BUBBLE +1F5E8 FE0F ; emoji style; # (7.0 🗨️ ) LEFT SPEECH BUBBLE +1F5EF FE0E ; text style; # (7.0 🗯︎ ) RIGHT ANGER BUBBLE +1F5EF FE0F ; emoji style; # (7.0 🗯️ ) RIGHT ANGER BUBBLE +1F5F3 FE0E ; text style; # (7.0 🗳︎ ) BALLOT BOX WITH BALLOT +1F5F3 FE0F ; emoji style; # (7.0 🗳️ ) BALLOT BOX WITH BALLOT +1F5FA FE0E ; text style; # (7.0 🗺︎ ) WORLD MAP +1F5FA FE0F ; emoji style; # (7.0 🗺️ ) WORLD MAP +1F610 FE0E ; text style; # (6.0 😐︎ ) NEUTRAL FACE +1F610 FE0F ; emoji style; # (6.0 😐️ ) NEUTRAL FACE +1F687 FE0E ; text style; # (6.0 🚇︎ ) METRO +1F687 FE0F ; emoji style; # (6.0 🚇️ ) METRO +1F68D FE0E ; text style; # (6.0 🚍︎ ) ONCOMING BUS +1F68D FE0F ; emoji style; # (6.0 🚍️ ) ONCOMING BUS +1F691 FE0E ; text style; # (6.0 🚑︎ ) AMBULANCE +1F691 FE0F ; emoji style; # (6.0 🚑️ ) AMBULANCE +1F694 FE0E ; text style; # (6.0 🚔︎ ) ONCOMING POLICE CAR +1F694 FE0F ; emoji style; # (6.0 🚔️ ) ONCOMING POLICE CAR +1F698 FE0E ; text style; # (6.0 🚘︎ ) ONCOMING AUTOMOBILE +1F698 FE0F ; emoji style; # (6.0 🚘️ ) ONCOMING AUTOMOBILE +1F6AD FE0E ; text style; # (6.0 🚭︎ ) NO SMOKING SYMBOL +1F6AD FE0F ; emoji style; # (6.0 🚭️ ) NO SMOKING SYMBOL +1F6B2 FE0E ; text style; # (6.0 🚲︎ ) BICYCLE +1F6B2 FE0F ; emoji style; # (6.0 🚲️ ) BICYCLE +1F6B9 FE0E ; text style; # (6.0 🚹︎ ) MENS SYMBOL +1F6B9 FE0F ; emoji style; # (6.0 🚹️ ) MENS SYMBOL +1F6BA FE0E ; text style; # (6.0 🚺︎ ) WOMENS SYMBOL +1F6BA FE0F ; emoji style; # (6.0 🚺️ ) WOMENS SYMBOL +1F6BC FE0E ; text style; # (6.0 🚼︎ ) BABY SYMBOL +1F6BC FE0F ; emoji style; # (6.0 🚼️ ) BABY SYMBOL +1F6CB FE0E ; text style; # (7.0 🛋︎ ) COUCH AND LAMP +1F6CB FE0F ; emoji style; # (7.0 🛋️ ) COUCH AND LAMP +1F6CD FE0E ; text style; # (7.0 🛍︎ ) SHOPPING BAGS +1F6CD FE0F ; emoji style; # (7.0 🛍️ ) SHOPPING BAGS +1F6CE FE0E ; text style; # (7.0 🛎︎ ) BELLHOP BELL +1F6CE FE0F ; emoji style; # (7.0 🛎️ ) BELLHOP BELL +1F6CF FE0E ; text style; # (7.0 🛏︎ ) BED +1F6CF FE0F ; emoji style; # (7.0 🛏️ ) BED +1F6E0 FE0E ; text style; # (7.0 🛠︎ ) HAMMER AND WRENCH +1F6E0 FE0F ; emoji style; # (7.0 🛠️ ) HAMMER AND WRENCH +1F6E1 FE0E ; text style; # (7.0 🛡︎ ) SHIELD +1F6E1 FE0F ; emoji style; # (7.0 🛡️ ) SHIELD +1F6E2 FE0E ; text style; # (7.0 🛢︎ ) OIL DRUM +1F6E2 FE0F ; emoji style; # (7.0 🛢️ ) OIL DRUM +1F6E3 FE0E ; text style; # (7.0 🛣︎ ) MOTORWAY +1F6E3 FE0F ; emoji style; # (7.0 🛣️ ) MOTORWAY +1F6E4 FE0E ; text style; # (7.0 🛤︎ ) RAILWAY TRACK +1F6E4 FE0F ; emoji style; # (7.0 🛤️ ) RAILWAY TRACK +1F6E5 FE0E ; text style; # (7.0 🛥︎ ) MOTOR BOAT +1F6E5 FE0F ; emoji style; # (7.0 🛥️ ) MOTOR BOAT +1F6E9 FE0E ; text style; # (7.0 🛩︎ ) SMALL AIRPLANE +1F6E9 FE0F ; emoji style; # (7.0 🛩️ ) SMALL AIRPLANE +1F6F0 FE0E ; text style; # (7.0 🛰︎ ) SATELLITE +1F6F0 FE0F ; emoji style; # (7.0 🛰️ ) SATELLITE +1F6F3 FE0E ; text style; # (7.0 🛳︎ ) PASSENGER SHIP +1F6F3 FE0F ; emoji style; # (7.0 🛳️ ) PASSENGER SHIP + +#Total sequences: 354 + +#EOF diff --git a/admin/unidata/emoji-zwj.awk b/admin/unidata/emoji-zwj.awk index 7d2ff6cb900..4b648aa675e 100644 --- a/admin/unidata/emoji-zwj.awk +++ b/admin/unidata/emoji-zwj.awk @@ -106,7 +106,8 @@ END { for (elt in ch) { - printf("(#x%s .\n,(eval-when-compile (regexp-opt\n'(\n%s\n))))\n", elt, vec[elt]) + entries = vec[elt] sprintf("\n\"\\N{U+%s}\\N{U+FE0E}\"\n\"\\N{U+%s}\\N{U+FE0F}\"", elt, elt) + printf("(#x%s .\n,(eval-when-compile (regexp-opt\n'(\n%s\n))))\n", elt, entries) } print "))" print " (set-char-table-range composition-function-table" diff --git a/doc/misc/calc.texi b/doc/misc/calc.texi index aacc0d0b0ec..f5f4dff0e18 100644 --- a/doc/misc/calc.texi +++ b/doc/misc/calc.texi @@ -6217,7 +6217,7 @@ method when it is able. @xref{Programming Answer 8, 8}. (@bullet{}) @cindex Gamma constant, Euler's @cindex Euler's gamma constant (@bullet{}) @strong{Exercise 9.} The @dfn{digamma} function -@texline @math{\psi(z) (``psi'')} +@texline @math{\psi(z)} (``psi'') @infoline @expr{psi(z)} is defined as the derivative of @texline @math{\ln \Gamma(z)}. diff --git a/doc/misc/transient.texi b/doc/misc/transient.texi index 330fdd1e3a2..be9e8698ab4 100644 --- a/doc/misc/transient.texi +++ b/doc/misc/transient.texi @@ -31,7 +31,7 @@ General Public License for more details. @finalout @titlepage @title Transient User and Developer Manual -@subtitle for version 0.4.0 +@subtitle for version 0.4.1 @author Jonas Bernoulli @page @vskip 0pt plus 1filll @@ -74,7 +74,7 @@ that hurdle is Psionic K's interactive tutorial, available at @end quotation @noindent -This manual is for Transient version 0.4.0. +This manual is for Transient version 0.4.1. @insertcopying @end ifnottex @@ -2150,7 +2150,7 @@ default value of a prefix using the same format as returned by @item @code{always-read} For options, whether to read a value on every invocation. -If this is nil, then options that have a value are simply unset and +If this is @code{nil}, then options that have a value are simply unset and have to be invoked a second time to set a new value. @item diff --git a/etc/NEWS.29 b/etc/NEWS.29 index a3457d70340..60aa64b5ede 100644 --- a/etc/NEWS.29 +++ b/etc/NEWS.29 @@ -1692,6 +1692,13 @@ the following to your Init file: *** New command 'dired-do-eww'. This command visits the file on the current line with EWW. +--- +*** 'browse-url-of-dired-file' can now call the secondary browser. +When invoked with a prefix arg, this will now call +'browse-url-secondary-browser-function' instead of the default +browser. 'browse-url-of-dired-file' is bound to 'W' by default in +dired mode. + --- *** New user option 'dired-omit-lines'. This is used by 'dired-omit-mode', and now allows you to hide based on @@ -1769,7 +1776,7 @@ or can take a long time to render. +++ *** New command 'enriched-toggle-markup'. This allows you to see the markup in 'enriched-mode' buffers (e.g., -the "HELLO" file). +the "HELLO" file). Bound to 'M-o m' by default. ** Shell Script Mode @@ -1879,7 +1886,9 @@ These commands can be useful if the ".elc" files are out of date +++ *** New DWIM action on 'x' in "*Packages*" buffer. If no packages are marked, 'x' will install the package under point if -it isn't already, and remove it if it is installed. +it isn't already, and remove it if it is installed. Customize the new +option 'package-menu-use-current-if-no-marks' to the nil value to get +back the old behavior of signaling an error in that case. +++ *** New command 'package-vc-install'. diff --git a/lisp/calendar/todo-mode.el b/lisp/calendar/todo-mode.el index 671210f3ee8..35cac5d7310 100644 --- a/lisp/calendar/todo-mode.el +++ b/lisp/calendar/todo-mode.el @@ -1985,7 +1985,13 @@ their associated keys and their effects." (setq done-only t) (todo-toggle-view-done-only)) (if here - (todo-insert-with-overlays new-item) + (progn + ;; Ensure item is inserted where command was invoked. + (unless (= (point) opoint) + (todo-category-number ocat) + (todo-category-select) + (goto-char opoint)) + (todo-insert-with-overlays new-item)) (todo-set-item-priority new-item cat t)) (setq item-added t)) ;; If user cancels before setting priority, restore @@ -2119,6 +2125,9 @@ the item at point." ((or marked (todo-item-string)) (todo-edit-item--next-key 'todo arg))))) +(defvar todo-edit-item--cat nil) +(defvar todo-edit-item--pos nil) + (defun todo-edit-item--text (&optional arg) "Function providing the text editing facilities of `todo-edit-item'." (let ((full-item (todo-item-string))) @@ -2127,6 +2136,7 @@ the item at point." ;; 1+ signals an error, so just make this a noop. (when full-item (let* ((opoint (point)) + (ocat (todo-current-category)) (start (todo-item-start)) (end (save-excursion (todo-item-end))) (item-beg (progn @@ -2151,8 +2161,7 @@ the item at point." (concat " \\[" (regexp-quote todo-comment-string) ": \\([^]]+\\)\\]") end t))) - (prompt (if comment "Edit comment: " "Enter a comment: ")) - (buffer-read-only nil)) + (prompt (if comment "Edit comment: " "Enter a comment: "))) ;; When there are marked items, user can invoke todo-edit-item ;; even if point is not on an item, but text editing only ;; applies to the item at point. @@ -2170,22 +2179,43 @@ the item at point." end t) (if comment-delete (when (todo-y-or-n-p "Delete comment? ") - (delete-region (match-beginning 0) (match-end 0))) - (replace-match (save-match-data - (read-string prompt - (cons (match-string 1) 1))) - nil nil nil 1)) + (let ((buffer-read-only nil)) + (delete-region (match-beginning 0) (match-end 0)))) + (let ((buffer-read-only nil)) + (replace-match (save-match-data + (prog1 (let ((buffer-read-only t)) + (read-string + prompt + (cons (match-string 1) 1))) + ;; If user moved point while editing + ;; a comment, restore it and ensure + ;; done items section is displayed. + (unless (= (point) opoint) + (todo-category-number ocat) + (let ((todo-show-with-done t)) + (todo-category-select) + (goto-char opoint))))) + nil nil nil 1))) (if comment-delete (user-error "There is no comment to delete") - (insert " [" todo-comment-string ": " - (prog1 (read-string prompt) - ;; If user moved point during editing, - ;; make sure it moves back. - (goto-char opoint) - (todo-item-end)) - "]"))))) + (let ((buffer-read-only nil)) + (insert " [" todo-comment-string ": " + (prog1 (let ((buffer-read-only t)) + (read-string prompt)) + ;; If user moved point while inserting a + ;; comment, restore it and ensure done items + ;; section is displayed. + (unless (= (point) opoint) + (todo-category-number ocat) + (let ((todo-show-with-done t)) + (todo-category-select) + (goto-char opoint))) + (todo-item-end)) + "]")))))) (multiline (let ((buf todo-edit-buffer)) + (setq todo-edit-item--cat ocat) + (setq todo-edit-item--pos opoint) (set-window-buffer (selected-window) (set-buffer (make-indirect-buffer (buffer-name) buf))) @@ -2208,10 +2238,14 @@ the item at point." ;; Ensure lines following hard newlines are indented. (setq new (replace-regexp-in-string "\\(\n\\)[^[:blank:]]" "\n\t" new nil nil 1)) - ;; If user moved point during editing, make sure it moves back. - (goto-char opoint) - (todo-remove-item) - (todo-insert-with-overlays new) + ;; If user moved point while editing item, restore it. + (unless (= (point) opoint) + (todo-category-number ocat) + (todo-category-select) + (goto-char opoint)) + (let ((buffer-read-only nil)) + (todo-remove-item) + (todo-insert-with-overlays new)) (move-to-column item-beg))))))))) (defun todo-edit-quit () @@ -2243,6 +2277,9 @@ made in the number or names of categories." (kill-buffer) (unless (eq (current-buffer) buf) (set-window-buffer (selected-window) (set-buffer buf))) + (todo-category-number todo-edit-item--cat) + (todo-category-select) + (goto-char todo-edit-item--pos) (if transient-mark-mode (deactivate-mark))) ;; We got here via `F e'. (when (todo-check-format) @@ -2315,117 +2352,118 @@ made in the number or names of categories." ;; If there are marked items, use only the first to set ;; header changes, and apply these to all marked items. (when first - (cond - ((eq what 'date) - (setq ndate (todo-read-date))) - ((eq what 'calendar) - (setq ndate (save-match-data (todo-set-date-from-calendar)))) - ((eq what 'today) - (setq ndate (calendar-date-string (calendar-current-date) t t))) - ((eq what 'dayname) - (setq ndate (todo-read-dayname))) - ((eq what 'time) - (setq ntime (save-match-data (todo-read-time))) - (when (> (length ntime) 0) - (setq ntime (concat " " ntime)))) - ;; When date string consists only of a day name, - ;; passing other date components is a noop. - ((and odayname (memq what '(year month day)))) - ((eq what 'year) - (setq day oday - monthname omonthname - month omonth - year (cond ((not current-prefix-arg) - (todo-read-date 'year)) - ((string= oyear "*") - (user-error "Cannot increment *")) - (t - (number-to-string (+ yy inc)))))) - ((eq what 'month) - (setf day oday - year oyear - (if (memq 'month calendar-date-display-form) - month - monthname) - (cond ((not current-prefix-arg) - (todo-read-date 'month)) - ((or (string= omonth "*") (= mm 13)) - (user-error "Cannot increment *")) - (t - (let* ((mmo mm) - ;; Change by 12 or more months? - (bigincp (>= (abs inc) 12)) - ;; Month number is in range 1..12. - (mminc (+ mm (% inc 12))) - (mm (% (+ mminc 12) 12)) - ;; 12n mod 12 = 0, so 0 is December. - (mm (if (= mm 0) 12 mm)) - ;; Does change in month cross year? - (mmcmp (cond ((< inc 0) (> mm mmo)) - ((> inc 0) (< mm mmo)))) - (yyadjust (if bigincp - (+ (abs (/ inc 12)) - (if mmcmp 1 0)) - 1))) - ;; Adjust year if necessary. - (setq yy (cond ((and (< inc 0) - (or mmcmp bigincp)) - (- yy yyadjust)) - ((and (> inc 0) - (or mmcmp bigincp)) - (+ yy yyadjust)) - (t yy))) - (setq year (number-to-string yy)) - ;; Return the changed numerical month as - ;; a string or the corresponding month name. - (if omonth - (number-to-string mm) - (aref tma-array (1- mm))))))) - ;; Since the number corresponding to the arbitrary - ;; month name "*" is out of the range of - ;; calendar-last-day-of-month, set it to 1 - ;; (corresponding to January) to allow 31 days. - (let ((mm (if (= mm 13) 1 mm))) - (if (> (string-to-number day) - (calendar-last-day-of-month mm yy)) - (user-error "%s %s does not have %s days" - (aref tmn-array (1- mm)) - (if (= mm 2) yy "") day)))) - ((eq what 'day) - (setq year oyear - month omonth - monthname omonthname - day (cond - ((not current-prefix-arg) - (todo-read-date 'day mm yy)) - ((string= oday "*") - (user-error "Cannot increment *")) - ((or (string= omonth "*") (string= omonthname "*")) - (setq dd (+ dd inc)) - (if (> dd 31) - (user-error - "A month cannot have more than 31 days") - (number-to-string dd))) - ;; Increment or decrement day by INC, - ;; adjusting month and year if necessary - ;; (if year is "*" assume current year to - ;; calculate adjustment). - (t - (let* ((yy (or yy (calendar-extract-year - (calendar-current-date)))) - (date (calendar-gregorian-from-absolute - (+ (calendar-absolute-from-gregorian - (list mm dd yy)) - inc))) - (adjmm (nth 0 date))) - ;; Set year and month(name) to adjusted values. - (unless (string= year "*") - (setq year (number-to-string (nth 2 date)))) - (if month - (setq month (number-to-string adjmm)) - (setq monthname (aref tma-array (1- adjmm)))) - ;; Return changed numerical day as a string. - (number-to-string (nth 1 date))))))))) + (save-match-data + (cond + ((eq what 'date) + (setq ndate (todo-read-date))) + ((eq what 'calendar) + (setq ndate (todo-set-date-from-calendar))) + ((eq what 'today) + (setq ndate (calendar-date-string (calendar-current-date) t t))) + ((eq what 'dayname) + (setq ndate (todo-read-dayname))) + ((eq what 'time) + (setq ntime (todo-read-time)) + (when (> (length ntime) 0) + (setq ntime (concat " " ntime)))) + ;; When date string consists only of a day name, + ;; passing other date components is a noop. + ((and odayname (memq what '(year month day)))) + ((eq what 'year) + (setq day oday + monthname omonthname + month omonth + year (cond ((not current-prefix-arg) + (todo-read-date 'year)) + ((string= oyear "*") + (user-error "Cannot increment *")) + (t + (number-to-string (+ yy inc)))))) + ((eq what 'month) + (setf day oday + year oyear + (if (memq 'month calendar-date-display-form) + month + monthname) + (cond ((not current-prefix-arg) + (todo-read-date 'month)) + ((or (string= omonth "*") (= mm 13)) + (user-error "Cannot increment *")) + (t + (let* ((mmo mm) + ;; Change by 12 or more months? + (bigincp (>= (abs inc) 12)) + ;; Month number is in range 1..12. + (mminc (+ mm (% inc 12))) + (mm (% (+ mminc 12) 12)) + ;; 12n mod 12 = 0, so 0 is December. + (mm (if (= mm 0) 12 mm)) + ;; Does change in month cross year? + (mmcmp (cond ((< inc 0) (> mm mmo)) + ((> inc 0) (< mm mmo)))) + (yyadjust (if bigincp + (+ (abs (/ inc 12)) + (if mmcmp 1 0)) + 1))) + ;; Adjust year if necessary. + (setq yy (cond ((and (< inc 0) + (or mmcmp bigincp)) + (- yy yyadjust)) + ((and (> inc 0) + (or mmcmp bigincp)) + (+ yy yyadjust)) + (t yy))) + (setq year (number-to-string yy)) + ;; Return the changed numerical month as + ;; a string or the corresponding month name. + (if omonth + (number-to-string mm) + (aref tma-array (1- mm))))))) + ;; Since the number corresponding to the arbitrary + ;; month name "*" is out of the range of + ;; calendar-last-day-of-month, set it to 1 + ;; (corresponding to January) to allow 31 days. + (let ((mm (if (= mm 13) 1 mm))) + (if (> (string-to-number day) + (calendar-last-day-of-month mm yy)) + (user-error "%s %s does not have %s days" + (aref tmn-array (1- mm)) + (if (= mm 2) yy "") day)))) + ((eq what 'day) + (setq year oyear + month omonth + monthname omonthname + day (cond + ((not current-prefix-arg) + (todo-read-date 'day mm yy)) + ((string= oday "*") + (user-error "Cannot increment *")) + ((or (string= omonth "*") (string= omonthname "*")) + (setq dd (+ dd inc)) + (if (> dd 31) + (user-error + "A month cannot have more than 31 days") + (number-to-string dd))) + ;; Increment or decrement day by INC, + ;; adjusting month and year if necessary + ;; (if year is "*" assume current year to + ;; calculate adjustment). + (t + (let* ((yy (or yy (calendar-extract-year + (calendar-current-date)))) + (date (calendar-gregorian-from-absolute + (+ (calendar-absolute-from-gregorian + (list mm dd yy)) + inc))) + (adjmm (nth 0 date))) + ;; Set year and month(name) to adjusted values. + (unless (string= year "*") + (setq year (number-to-string (nth 2 date)))) + (if month + (setq month (number-to-string adjmm)) + (setq monthname (aref tma-array (1- adjmm)))) + ;; Return changed numerical day as a string. + (number-to-string (nth 1 date)))))))))) (unless odayname ;; If year, month or day date string components were ;; changed, rebuild the date string. diff --git a/lisp/composite.el b/lisp/composite.el index 06c7c174163..3ae3e64d5b9 100644 --- a/lisp/composite.el +++ b/lisp/composite.el @@ -861,7 +861,7 @@ and the second is a glyph for a variation selector." ;; handled in font_range, we end up choosing the Emoji presentation ;; rather than the Text presentation. (let ((elt '([".." 1 compose-gstring-for-variation-glyph]))) - (set-char-table-range composition-function-table '(#xFE00 . #xFE0E) elt) + (set-char-table-range composition-function-table '(#xFE00 . #xFE0D) elt) (set-char-table-range composition-function-table '(#xE0100 . #xE01EF) elt)) (defun auto-compose-chars (func from to font-object string direction) diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el index 69595601bc8..ba0e3618f28 100644 --- a/lisp/emacs-lisp/package.el +++ b/lisp/emacs-lisp/package.el @@ -3317,6 +3317,18 @@ Values can be interactively added to this list by typing :version "25.1" :type '(repeat (regexp :tag "Hide packages with name matching"))) +(defcustom package-menu-use-current-if-no-marks t + "Whether \\\\[package-menu-execute] in package menu operates on current package if none are marked. + +If non-nil, and no packages are marked for installation or +deletion, \\\\[package-menu-execute] will operate on the current package at point, +see `package-menu-execute' for details. +The default is t. Set to nil to get back the original behavior +of having `package-menu-execute' signal an error when no packages +are marked for installation or deletion." + :version "29.1" + :type 'boolean) + (defun package-menu--refresh (&optional packages keywords) "Re-populate the `tabulated-list-entries'. PACKAGES should be nil or t, which means to display all known packages. @@ -3760,8 +3772,8 @@ object corresponding to the newer version." (and avail-pkg (version-list-< (package-desc-priority-version pkg-desc) (package-desc-priority-version avail-pkg)) - (xor (not package-install-upgrade-built-in) - (package--active-built-in-p pkg-desc)) + (or (not (package--active-built-in-p pkg-desc)) + package-install-upgrade-built-in) (push (cons name avail-pkg) upgrades)))) upgrades)) @@ -3946,7 +3958,8 @@ invocations." ;; Nothing marked. (unless (or delete-list install-list) ;; Not on a package line. - (unless (tabulated-list-get-id) + (unless (and (tabulated-list-get-id) + package-menu-use-current-if-no-marks) (user-error "No operations specified")) (let* ((id (tabulated-list-get-id)) (status (package-menu-get-status))) diff --git a/lisp/info-look.el b/lisp/info-look.el index 7858ed58774..da45e30cd36 100644 --- a/lisp/info-look.el +++ b/lisp/info-look.el @@ -733,7 +733,11 @@ Return nil if there is nothing appropriate in the buffer near point." (let ((str (string-join str-list " "))) (when (assoc str completions) (throw 'result str)) - (nbutlast str-list))))))) + ;; 'nbutlast' will not destructively set its argument + ;; to nil when the argument is a list of 1 element. + (if (= (length str-list) 1) + (setq str-list nil) + (nbutlast str-list)))))))) (error nil))) ;;;###autoload diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 36fff1520ac..30d2c24e19e 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -2021,11 +2021,14 @@ completions." (defcustom completions-header-format (propertize "%s possible completions:\n" 'face 'shadow) - "Format of completions header. -It may contain one %s to show the total count of completions. -When nil, no header is shown." - :type '(choice (const :tag "No header" nil) - (string :tag "Header format string")) + "If non-nil, the format string for completions heading line. +The heading line is inserted before the completions, and is intended +to summarize the completions. +The format string may include one %s, which will be replaced with +the total count of possible completions. +If this is nil, no heading line will be shown." + :type '(choice (const :tag "No heading line" nil) + (string :tag "Format string for heading line")) :version "29.1") (defun completion--insert-strings (strings &optional group-fun) diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el index c2629b69d59..8036bbc8acc 100644 --- a/lisp/net/browse-url.el +++ b/lisp/net/browse-url.el @@ -829,10 +829,17 @@ If optional arg TEMP-FILE-NAME is non-nil, delete it instead." (&optional localp no-error-if-not-filep)) ;;;###autoload -(defun browse-url-of-dired-file () - "In Dired, ask a WWW browser to display the file named on this line." - (interactive) +(defun browse-url-of-dired-file (&optional secondary) + "In Dired, ask a WWW browser to display the file named on this line. +With prefix arg, use the secondary browser instead (e.g. EWW if +`browse-url-secondary-browser-function' is set to +`eww-browse-url'." + (interactive "P") (let ((tem (dired-get-filename t t)) + (browse-url-browser-function + (if secondary + browse-url-secondary-browser-function + browse-url-browser-function)) ;; Some URL handlers open files in Emacs. We want to always ;; open in a browser, so disable those. (browse-url-default-handlers nil)) diff --git a/lisp/plstore.el b/lisp/plstore.el index 0276a752a0f..d18d461d7d1 100644 --- a/lisp/plstore.el +++ b/lisp/plstore.el @@ -24,6 +24,14 @@ ;; Plist based data store providing search and partial encryption. ;; +;; By default, this package uses symmetric encryption, which means +;; that you have to enter the password protecting your store more +;; often than you probably expect to. To use public key encryption +;; with this package, create a GnuPG key and customize user option +;; `plstore-encrypt-to' to use it. You can then configure the GnuPG +;; agent to adjust caching and expiration of the passphrase for your +;; store. +;; ;; Creating: ;; ;; ;; Open a new store associated with ~/.emacs.d/auth.plist. @@ -43,12 +51,16 @@ ;; ;; Kill the buffer visiting ~/.emacs.d/auth.plist. ;; (plstore-close store) ;; +;; Avoid marking one property both as public *and* secret, as the +;; behavior of this package with respect to such duplicate properties +;; is not (yet) defined. +;; ;; Searching: ;; ;; (setq store (plstore-open (expand-file-name "~/.emacs.d/auth.plist"))) ;; ;; ;; As the entry "foo" associated with "foo.example.org" has no -;; ;; secret properties, no need to decryption. +;; ;; secret properties, no need for decryption. ;; (plstore-find store '(:host ("foo.example.org"))) ;; ;; ;; As the entry "bar" associated with "bar.example.org" has a @@ -66,17 +78,119 @@ ;; Editing: ;; ;; This file also provides `plstore-mode', a major mode for editing -;; the PLSTORE format file. Visit a non-existing file and put the +;; the plstore format file. Visit a non-existing file and put the ;; following line: ;; ;; (("foo" :host "foo.example.org" :secret-user "user")) ;; ;; where the prefixing `:secret-' means the property (without ;; `:secret-' prefix) is marked as secret. Thus, when you save the -;; buffer, the `:secret-user' property is encrypted as `:user'. +;; buffer, the `:secret-user' property is encrypted as `:user'. Do +;; not use a property consisting solely of the prefix, as the behavior +;; of this package with respect to such properties is not (yet) +;; defined. ;; ;; You can toggle the view between encrypted form and the decrypted ;; form with C-c C-c. +;; +;; If you have opened a plstore with `plstore-open' you should not +;; edit its underlying buffer in `plstore-mode' or in any other way at +;; the same time, since your manual changes will be overwritten when +;; `plstore-save' is called on that plstore. +;; +;; Internals: +;; +;; This is information on the internal data structure and functions of +;; this package. None of it should be necessary to actually use it. +;; For easier reading, we usually do not distinguish in this internal +;; documentation between a Lisp object and its printed representation. +;; +;; A plstore corresponds to an alist mapping strings to property +;; lists. Internally, that alist is organized as two alists, one +;; mapping to the non-secret properties and placeholders for the +;; secret properties (called "template alist" with identifier ALIST) +;; and one mapping to the secret properties ("secret alist", +;; SECRET-ALIST). The secret alist is read from and written to file +;; as pgp-encrypted printed representation of the alist ("encrypted +;; data", ENCRYPTED-DATA). +;; +;; During the lifetime of a plstore, a third type of alist may pop up, +;; which maps to the merged non-secret properties and plain-text +;; secret properties ("merged alist", MERGED-ALIST). +;; +;; After executing the "foo", "bar", "baz" example from above the +;; alists described above look like the following: +;; +;; Template Alist: +;; +;; (("foo" :host "foo.example.org" :port 80) +;; ("bar" :secret-user t :host "bar.example.org") +;; ("baz" :secret-password t :host "baz.example.org")) +;; +;; Secret Alist: +;; +;; (("bar" :user "test") +;; ("baz" :password "test")) +;; +;; Merged Alist: +;; +;; (("foo" :host "foo.example.org" :port 80) +;; ("bar" :user "test" :host "bar.example.org") +;; ("baz" :password "test" :host "baz.example.org")) +;; +;; Finally, a plstore requires a buffer ("plstore buffer", BUFFER) for +;; conversion between its Lisp objects and its file representation. +;; It is important to note that this buffer is *not* continuously +;; synchronized as the plstore changes. During the lifetime of a +;; plstore, its buffer is read from in function `plstore-open' and +;; (destructively) written to in `plstore-save', but not touched +;; otherwise. We call the file visited by the plstore buffer the +;; associated file of the plstore. +;; +;; With the identifiers defined above a plstore is a vector with the +;; following elements and accessor functions: +;; +;; [ +;; BUFFER ; plstore--get/set-buffer +;; ALIST ; plstore--get/set-alist +;; ENCRYPTED-DATA ; plstore--get/set-encrypted-data +;; SECRET-ALIST ; plstore--get/set-secret-alist +;; MERGED-ALIST ; plstore--get/set-merged-alist +;; ] +;; +;; When a plstore is created through `plstore-open', its ALIST and +;; ENCRYPTED-DATA are initialized from the contents of BUFFER without +;; any decryption taking place, and MERGED-ALIST is initialized as a +;; copy of ALIST. (Which means that at that stage the merged alist +;; still contains the secret property placeholders!) +;; +;; During on-demand decryption of a plstore through function +;; `plstore--decrypt', SECRET-ALIST is populated from ENCRYPTED-DATA, +;; which is in turn replaced by value nil. (Which further serves as +;; an indicator that the plstore has been decrypted already.) In +;; addition, MERGED-ALIST is recomputed by function +;; `plstore--merge-secret' to replace the secret property placeholders +;; by their plain-text secret property equivalents. +;; +;; The file representation of a plstore consists of two Lisp forms plus +;; markers to introduce them: +;; +;; ;;; public entries +;; ALIST +;; ;;; secret entries +;; ENCRYPTED-DATA +;; +;; Both of these are optional, but the first section must be present +;; if the second one is. If both sections are missing, the plstore is +;; empty. If the second section is missing, it contains only +;; non-secret data. If present, the printed representation of the +;; encrypted data includes the delimiting double quotes. +;; +;; The plstore API (`plstore-open', `plstore-put', etc.) and the +;; plstore mode implemented by `plstore-mode' are orthogonal to each +;; other and should not be mixed up. In particular, encoding and +;; decoding a plstore mode buffer with `plstore-mode-toggle-display' +;; is not related in any way to the state of the plstore buffer. ;;; Code: @@ -121,10 +235,13 @@ symmetric encryption will be used." (put 'plstore-encrypt-to 'permanent-local t) -(defvar plstore-encoded nil) +(defvar plstore-encoded nil + "Non-nil if the current buffer shows the decoded alist.") ; [sic!] (put 'plstore-encoded 'permanent-local t) +;;; EasyPG callback functions. + (defvar plstore-cache-passphrase-for-symmetric-encryption nil) (defvar plstore-passphrase-alist nil) @@ -141,11 +258,11 @@ symmetric encryption will be used." (cons entry plstore-passphrase-alist))) (setq passphrase - (read-passwd (format "Passphrase for PLSTORE %s: " + (read-passwd (format "Passphrase for plstore %s: " (plstore--get-buffer plstore)))) (setcdr entry (copy-sequence passphrase)) passphrase))) - (read-passwd (format "Passphrase for PLSTORE %s: " + (read-passwd (format "Passphrase for plstore %s: " (plstore--get-buffer plstore))))) (defun plstore-progress-callback-function (_context _what _char current total @@ -155,6 +272,8 @@ symmetric encryption will be used." (message "%s...%d%%" handback (if (> total 0) (floor (* (/ current (float total)) 100)) 0)))) +;;; Core functions. + (defun plstore--get-buffer (arg) (aref arg 0)) @@ -193,6 +312,7 @@ symmetric encryption will be used." (vector buffer alist encrypted-data secret-alist merged-alist)) (defun plstore--init-from-buffer (plstore) + "Parse current buffer and initialize PLSTORE from it." (goto-char (point-min)) (when (looking-at ";;; public entries") (forward-line) @@ -223,16 +343,20 @@ symmetric encryption will be used." store))) (defun plstore-revert (plstore) - "Replace current data in PLSTORE with the file on disk." + "Replace current data in PLSTORE from its associated file." (with-current-buffer (plstore--get-buffer plstore) (revert-buffer t t) (plstore--init-from-buffer plstore))) (defun plstore-close (plstore) - "Destroy a plstore instance PLSTORE." + "Destroy plstore instance PLSTORE." (kill-buffer (plstore--get-buffer plstore))) (defun plstore--merge-secret (plstore) + "Determine the merged alist of PLSTORE. +Create the merged alist as a copy of the template alist with all +placeholder properties that have corresponding properties in the +secret alist replaced by their plain-text secret properties." (let ((alist (plstore--get-secret-alist plstore)) modified-alist modified-plist @@ -251,19 +375,26 @@ symmetric encryption will be used." modified-entry (assoc (car entry) modified-alist) modified-plist (cdr modified-entry)) (while plist + ;; Search for a placeholder property in the merged alist + ;; corresponding to the current secret property. (setq placeholder (plist-member modified-plist (intern (concat ":secret-" (substring (symbol-name (car plist)) 1))))) + ;; Replace its name with the real, secret property name. (if placeholder (setcar placeholder (car plist))) + ;; Update its value to the plain-text secret property value. (setq modified-plist (plist-put modified-plist (car plist) (car (cdr plist)))) (setq plist (nthcdr 2 plist))) (setcdr modified-entry modified-plist)))) (defun plstore--decrypt (plstore) + "Decrypt the encrypted data of PLSTORE. +Update its internal alists and other data structures +accordingly." (if (plstore--get-encrypted-data plstore) (let ((context (epg-make-context 'OpenPGP)) plain) @@ -290,6 +421,11 @@ symmetric encryption will be used." (plstore--set-encrypted-data plstore nil)))) (defun plstore--match (entry keys skip-if-secret-found) + "Return whether plist KEYS matches ENTRY. +ENTRY should be a key of the merged alist of a PLSTORE. This +function returns nil if KEYS do not match ENTRY, t if they match, +and symbol `secret' if the secret alist needs to be consulted to +perform a match." (let ((result t) key-name key-value prop-value secret-name) (while keys (setq key-name (car keys) @@ -311,11 +447,10 @@ symmetric encryption will be used." result)) (defun plstore-find (plstore keys) - "Perform search on PLSTORE with KEYS. -KEYS is a plist." + "Return all PLSTORE entries matching plist KEYS." (let (entries alist entry match decrypt plist) - ;; First, go through the merged plist alist and collect entries - ;; matched with keys. + ;; First, go through the merged alist and collect entries matched + ;; by the keys. (setq alist (plstore--get-merged-alist plstore)) (while alist (setq entry (car alist) @@ -331,7 +466,7 @@ KEYS is a plist." plist nil)) (setq plist (nthcdr 2 plist))) (setq entries (cons entry entries))))) - ;; Second, decrypt the encrypted plist and try again. + ;; Second, decrypt the plstore and try again. (when decrypt (setq entries nil) (plstore--decrypt plstore) @@ -345,7 +480,8 @@ KEYS is a plist." (nreverse entries))) (defun plstore-get (plstore name) - "Get an entry with NAME in PLSTORE." + "Return the entry named NAME in PLSTORE. +Return nil if there is none." (let ((entry (assoc name (plstore--get-merged-alist plstore))) plist) (setq plist (cdr entry)) @@ -359,7 +495,7 @@ KEYS is a plist." entry)) (defun plstore-put (plstore name keys secret-keys) - "Put an entry with NAME in PLSTORE. + "Put an entry named NAME in PLSTORE. KEYS is a plist containing non-secret data. SECRET-KEYS is a plist containing secret data." (let (entry @@ -398,7 +534,7 @@ SECRET-KEYS is a plist containing secret data." (plstore--merge-secret plstore))) (defun plstore-delete (plstore name) - "Delete an entry with NAME from PLSTORE." + "Delete the first entry named NAME from PLSTORE." (let ((entry (assoc name (plstore--get-alist plstore)))) (if entry (plstore--set-alist @@ -417,6 +553,8 @@ SECRET-KEYS is a plist containing secret data." (defvar pp-escape-newlines) (defun plstore--insert-buffer (plstore) + "Insert the file representation of PLSTORE at point. +Assumes that PLSTORE has been decrypted." (insert ";;; public entries -*- mode: plstore -*- \n" (pp-to-string (plstore--get-alist plstore))) (if (plstore--get-secret-alist plstore) @@ -451,13 +589,31 @@ If no one is selected, symmetric encryption will be performed. " (insert ";;; secret entries\n" (pp-to-string cipher))))) (defun plstore-save (plstore) - "Save the contents of PLSTORE associated with a FILE." + "Save PLSTORE to its associated file." (with-current-buffer (plstore--get-buffer plstore) (erase-buffer) (plstore--insert-buffer plstore) (save-buffer))) +;;; plstore mode. + +;; The functions related to plstore mode unfortunately introduce yet +;; another alist format ("decoded alist"). After executing the "foo", +;; "bar", "baz" example from above the decoded alist of the plstore +;; would look like the following: +;; +;; (("foo" :host "foo.example.org" :port 80) +;; ("bar" :secret-user "test" :host "bar.example.org") +;; ("baz" :secret-password "test" :host "baz.example.org")) +;; +;; Even more unfortunately, variable and function names of the +;; following are a bit mixed up IMHO: With the current names, the +;; result of function `plstore--encode' is used to create what is +;; presented as "decoded form of a plstore" to the user. And variable +;; `plstore-encoded' is non-nil if a buffer shows the decoded form. + (defun plstore--encode (plstore) + "Return the printed representation of the decoded alist of PLSTORE." (plstore--decrypt plstore) (let ((merged-alist (plstore--get-merged-alist plstore))) (concat "(" @@ -482,6 +638,9 @@ If no one is selected, symmetric encryption will be performed. " ")"))) (defun plstore--decode (string) + "Create a plstore instance from STRING. +STRING should be the printed representation of a decoded alist of +some plstore." (let* ((alist (car (read-from-string string))) (pointer alist) secret-alist @@ -489,7 +648,7 @@ If no one is selected, symmetric encryption will be performed. " entry) (while pointer (unless (stringp (car (car pointer))) - (error "Invalid PLSTORE format %s" string)) + (error "Invalid plstore format %s" string)) (setq plist (cdr (car pointer))) (while plist (when (string-match "\\`:secret-" (symbol-name (car plist))) @@ -509,6 +668,10 @@ If no one is selected, symmetric encryption will be performed. " (plstore--make nil alist nil secret-alist))) (defun plstore--write-contents-functions () + "Convert the decoded form of a plstore in the current buffer. +Convert it to the regular file representation of a plstore if +needed. This function is used on hook `write-contents-functions' +in plstore mode buffers." (when plstore-encoded (let ((store (plstore--decode (buffer-string))) (file (buffer-file-name))) @@ -546,7 +709,7 @@ If no one is selected, symmetric encryption will be performed. " (erase-buffer) (insert (substitute-command-keys "\ -;;; You are looking at the decoded form of the PLSTORE file.\n\ +;;; You are looking at the decoded form of the plstore file.\n\ ;;; To see the original form content, do \\[plstore-mode-toggle-display]\n\n")) (insert (plstore--encode store)) (set-buffer-modified-p nil) @@ -561,7 +724,7 @@ If no one is selected, symmetric encryption will be performed. " ;;;###autoload (define-derived-mode plstore-mode emacs-lisp-mode "PLSTORE" - "Major mode for editing PLSTORE files." + "Major mode for editing plstore files." (make-local-variable 'plstore-encoded) (add-hook 'write-contents-functions #'plstore--write-contents-functions) (define-key plstore-mode-map "\C-c\C-c" #'plstore-mode-toggle-display) diff --git a/lisp/progmodes/dockerfile-ts-mode.el b/lisp/progmodes/dockerfile-ts-mode.el index c9125bc6cbd..333158e20f6 100644 --- a/lisp/progmodes/dockerfile-ts-mode.el +++ b/lisp/progmodes/dockerfile-ts-mode.el @@ -123,8 +123,9 @@ continuation to the previous entry." (let* ((node (treesit-buffer-root-node)) (stage-tree (treesit-induce-sparse-tree node "from_instruction" - nil 1000))) - `(("Stage" . ,(dockerfile-ts-mode--imenu-1 stage-tree))))) + nil 1000)) + (stage-index (dockerfile-ts-mode--imenu-1 stage-tree))) + (when stage-index `(("Stage" . ,stage-index))))) (defun dockerfile-ts-mode--imenu-1 (node) "Helper for `dockerfile-ts-mode--imenu'. diff --git a/lisp/progmodes/go-ts-mode.el b/lisp/progmodes/go-ts-mode.el index b8705ecc4d0..32d86f44235 100644 --- a/lisp/progmodes/go-ts-mode.el +++ b/lisp/progmodes/go-ts-mode.el @@ -59,6 +59,7 @@ (modify-syntax-entry ?< "." table) (modify-syntax-entry ?> "." table) (modify-syntax-entry ?\\ "\\" table) + (modify-syntax-entry ?\' "\"" table) (modify-syntax-entry ?/ ". 124b" table) (modify-syntax-entry ?* ". 23" table) (modify-syntax-entry ?\n "> b" table) diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el index 47d87112ffb..8a396a8c977 100644 --- a/lisp/progmodes/java-ts-mode.el +++ b/lisp/progmodes/java-ts-mode.el @@ -226,6 +226,9 @@ the available version of Tree-sitter for java." (constructor_declaration name: (identifier) @font-lock-type-face) + (compact_constructor_declaration + name: (identifier) @font-lock-type-face) + (field_access object: (identifier) @font-lock-type-face) diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 41a5c976629..56c524bcab5 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -816,8 +816,8 @@ DIRS must contain directory names." (push buf bufs))) (nreverse bufs))) -(cl-defmethod project-name ((_project (head vc))) - (or project-vc-name +(cl-defmethod project-name ((project (head vc))) + (or (project--value-in-dir 'project-vc-name (project-root project)) (cl-call-next-method))) diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 8c716ffb313..86bbf51dab7 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -415,7 +415,6 @@ instead." "Python mode specialized rx macro. This variant of `rx' supports common Python named REGEXPS." `(rx-let ((sp-bsnl (or space (and ?\\ ?\n))) - (sp-nl (or space (and (? ?\\) ?\n))) (block-start (seq symbol-start (or "def" "class" "if" "elif" "else" "try" "except" "finally" "for" "while" "with" @@ -650,9 +649,9 @@ the {...} holes that appear within f-strings." finally return (and result-valid result)))) (defvar python-font-lock-keywords-level-1 - `((,(python-rx symbol-start "def" (1+ sp-bsnl) (group symbol-name)) + `((,(python-rx symbol-start "def" (1+ space) (group symbol-name)) (1 font-lock-function-name-face)) - (,(python-rx symbol-start "class" (1+ sp-bsnl) (group symbol-name)) + (,(python-rx symbol-start "class" (1+ space) (group symbol-name)) (1 font-lock-type-face))) "Font lock keywords to use in `python-mode' for level 1 decoration. @@ -792,12 +791,12 @@ sign in chained assignment." ;; [*a] = 5, 6 ;; are handled separately below (,(python-font-lock-assignment-matcher - (python-rx (? (or "[" "(") (* sp-nl)) - grouped-assignment-target (* sp-nl) ?, (* sp-nl) - (* assignment-target (* sp-nl) ?, (* sp-nl)) - (? assignment-target (* sp-nl)) - (? ?, (* sp-nl)) - (? (or ")" "]") (* sp-bsnl)) + (python-rx (? (or "[" "(") (* space)) + grouped-assignment-target (* space) ?, (* space) + (* assignment-target (* space) ?, (* space)) + (? assignment-target (* space)) + (? ?, (* space)) + (? (or ")" "]") (* space)) (group assignment-operator))) (1 font-lock-variable-name-face) (2 'font-lock-operator-face) @@ -813,9 +812,9 @@ sign in chained assignment." ;; c: Collection = {1, 2, 3} ;; d: Mapping[int, str] = {1: 'bar', 2: 'baz'} (,(python-font-lock-assignment-matcher - (python-rx (or line-start ?\;) (* sp-bsnl) - grouped-assignment-target (* sp-bsnl) - (? ?: (* sp-bsnl) (+ not-simple-operator) (* sp-bsnl)) + (python-rx (or line-start ?\;) (* space) + grouped-assignment-target (* space) + (? ?: (* space) (+ not-simple-operator) (* space)) (group assignment-operator))) (1 font-lock-variable-name-face) (2 'font-lock-operator-face)) @@ -824,10 +823,10 @@ sign in chained assignment." ;; [a] = 5, ;; [*a] = 5, 6 (,(python-font-lock-assignment-matcher - (python-rx (or line-start ?\; ?=) (* sp-bsnl) - (or "[" "(") (* sp-nl) - grouped-assignment-target (* sp-nl) - (or ")" "]") (* sp-bsnl) + (python-rx (or line-start ?\; ?=) (* space) + (or "[" "(") (* space) + grouped-assignment-target (* space) + (or ")" "]") (* space) (group assignment-operator))) (1 font-lock-variable-name-face) (2 'font-lock-operator-face)) @@ -869,22 +868,6 @@ decorators, exceptions, and assignments.") Which one will be chosen depends on the value of `font-lock-maximum-decoration'.") -(defvar font-lock-beg) -(defvar font-lock-end) -(defun python-font-lock-extend-region () - "Extend font-lock region to statement boundaries." - (let ((beg font-lock-beg) - (end font-lock-end)) - (goto-char beg) - (python-nav-beginning-of-statement) - (beginning-of-line) - (when (< (point) beg) - (setq font-lock-beg (point))) - (goto-char end) - (python-nav-end-of-statement) - (when (< end (point)) - (setq font-lock-end (point))) - (or (/= beg font-lock-beg) (/= end font-lock-end)))) (defconst python-syntax-propertize-function (syntax-propertize-rules @@ -6171,7 +6154,8 @@ Optional argument REGEXP selects variables to clone and defaults to \"^python-\"." (mapc (lambda (pair) - (and (symbolp (car pair)) + (and (consp pair) + (symbolp (car pair)) (string-match (or regexp "^python-") (symbol-name (car pair))) (set (make-local-variable (car pair)) @@ -6769,8 +6753,6 @@ implementations: `python-mode' and `python-ts-mode'." nil nil nil nil (font-lock-syntactic-face-function . python-font-lock-syntactic-face-function))) - (add-hook 'font-lock-extend-region-functions - #'python-font-lock-extend-region nil t) (setq-local syntax-propertize-function python-syntax-propertize-function) (setq-local imenu-create-index-function diff --git a/lisp/progmodes/rust-ts-mode.el b/lisp/progmodes/rust-ts-mode.el index be06acde3e3..c3cf8d0cf44 100644 --- a/lisp/progmodes/rust-ts-mode.el +++ b/lisp/progmodes/rust-ts-mode.el @@ -350,7 +350,12 @@ Return nil if there is no name or if NODE is not a defun node." (treesit-node-child-by-field-name node "name") t)))) (defun rust-ts-mode--syntax-propertize (beg end) - "Apply syntax text property to template delimiters between BEG and END. + "Apply syntax properties to special characters between BEG and END. + +Apply syntax properties to various special characters with +contextual meaning between BEG and END. + +The apostrophe \\=' should be treated as string when used for char literals. < and > are usually punctuation, e.g., as greater/less-than. But when used for types, they should be considered pairs. @@ -359,11 +364,18 @@ This function checks for < and > in the changed RANGES and apply appropriate text property to alter the syntax of template delimiters < and >'s." (goto-char beg) + (while (search-forward "'" end t) + (when (string-equal "char_literal" + (treesit-node-type + (treesit-node-at (match-beginning 0)))) + (put-text-property (match-beginning 0) (match-end 0) + 'syntax-table (string-to-syntax "\"")))) + (goto-char beg) (while (re-search-forward (rx (or "<" ">")) end t) (pcase (treesit-node-type (treesit-node-parent (treesit-node-at (match-beginning 0)))) - ("type_arguments" + ((or "type_arguments" "type_parameters") (put-text-property (match-beginning 0) (match-end 0) 'syntax-table diff --git a/lisp/progmodes/typescript-ts-mode.el b/lisp/progmodes/typescript-ts-mode.el index 3f198e9f180..8255e82a99e 100644 --- a/lisp/progmodes/typescript-ts-mode.el +++ b/lisp/progmodes/typescript-ts-mode.el @@ -128,7 +128,7 @@ Argument LANGUAGE is either `typescript' or `tsx'." "case" "catch" "class" "const" "continue" "debugger" "declare" "default" "delete" "do" "else" "enum" "export" "extends" "finally" "for" "from" "function" - "get" "if" "implements" "import" "in" "instanceof" "interface" + "get" "if" "implements" "import" "in" "instanceof" "interface" "is" "keyof" "let" "namespace" "new" "of" "private" "protected" "public" "readonly" "return" "set" "static" "switch" "target" "throw" "try" "type" "typeof" "var" "void" diff --git a/lisp/simple.el b/lisp/simple.el index d23e2e20c62..5ae92c6e4a0 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -1520,7 +1520,8 @@ the actual saved text might be different from what was killed." (let ((from (car cmp)) (to (cadr cmp))) (cond - ((= (length cmp) 2) ; static composition + ((and (= (length cmp) 3) ; static composition + (booleanp (nth 2 cmp))) to) ;; TO can be at POS, in which case we want ;; to make sure we advance at least by 1 diff --git a/lisp/textmodes/enriched.el b/lisp/textmodes/enriched.el index ec3046decd4..92be5a52bf2 100644 --- a/lisp/textmodes/enriched.el +++ b/lisp/textmodes/enriched.el @@ -192,6 +192,7 @@ The value is a list of \(VAR VALUE VAR VALUE...).") (define-key map "\C-c[" #'set-left-margin) (define-key map "\C-c]" #'set-right-margin) (define-key map "\M-o" #'facemenu-keymap) + (define-key map "\M-om" #'enriched-toggle-markup) map) "Keymap for Enriched mode.") diff --git a/lisp/tmm.el b/lisp/tmm.el index 1f9a877c20b..a4058594622 100644 --- a/lisp/tmm.el +++ b/lisp/tmm.el @@ -28,6 +28,7 @@ ;;; Code: (require 'electric) +(require 'text-property-search) (defgroup tmm nil "Text mode access to menu-bar." @@ -169,9 +170,11 @@ instead of executing it." (error "Empty menu reached")) (and tmm-km-list (let ((index-of-default 0)) - (if tmm-mid-prompt - (setq tmm-km-list (tmm-add-shortcuts tmm-km-list)) - t) + (setq tmm-km-list + (if tmm-mid-prompt + (tmm-add-shortcuts tmm-km-list) + ;; tmm-add-shortcuts reverses tmm-km-list internally. + (reverse tmm-km-list))) ;; Find the default item's index within the menu bar. ;; We use this to decide the initial minibuffer contents ;; and initial history position. @@ -192,7 +195,11 @@ instead of executing it." (or (not visible) (eval visible)))))) (setq index-of-default (1+ index-of-default))) (setq tail (cdr tail))))) - (let ((prompt (concat "^." (regexp-quote tmm-mid-prompt)))) + (let ((prompt + (concat "^" + (if (stringp tmm-mid-prompt) + (concat "." + (regexp-quote tmm-mid-prompt)))))) (setq tmm--history (reverse (delq nil (mapcar @@ -320,8 +327,22 @@ Stores a list of all the shortcuts in the free variable `tmm-short-cuts'." (defun tmm-completion-delete-prompt () (with-current-buffer standard-output - (goto-char (point-min)) - (delete-region (point) (search-forward "Possible completions are:\n")))) + (goto-char (point-min)) + (let* (;; First candidate: first string with mouse-face + (menu-start-1 (or (and (get-text-property (point) 'mouse-face) (point)) + (next-single-char-property-change (point) 'mouse-face))) + ;; Second candidate: an inactive menu item with tmm-inactive face + (tps-result (save-excursion + (text-property-search-forward 'face 'tmm-inactive t))) + (menu-start-2 (and tps-result (prop-match-beginning tps-result)))) + (or (and (null menu-start-1) (null menu-start-2)) + (delete-region (point) + ;; Use the smallest position of the two candidates. + (or (and menu-start-1 menu-start-2 + (min menu-start-1 menu-start-2)) + ;; Otherwise use the one that is non-nil. + menu-start-1 + menu-start-2)))))) (defun tmm-remove-inactive-mouse-face () "Remove the mouse-face property from inactive menu items." diff --git a/lisp/transient.el b/lisp/transient.el index 1d763c4ddeb..048554eee13 100644 --- a/lisp/transient.el +++ b/lisp/transient.el @@ -6,7 +6,7 @@ ;; URL: https://github.com/magit/transient ;; Keywords: extensions -;; Package-Version: 0.4.0 +;; Package-Version: 0.4.1 ;; Package-Requires: ((emacs "26.1")) ;; SPDX-License-Identifier: GPL-3.0-or-later @@ -1643,6 +1643,7 @@ of the corresponding object." "" #'transient--do-stay "" #'transient--do-stay "" #'transient--do-stay + "" #'transient--do-stay "" #'transient--do-minus "" #'transient--do-stay "" #'transient--do-quit-all @@ -3043,10 +3044,12 @@ prompt." (progn (cl-call-next-method obj value) (dolist (arg incomp) - (when-let ((obj (cl-find-if (lambda (obj) - (and (slot-boundp obj 'argument) - (equal (oref obj argument) arg))) - transient--suffixes))) + (when-let ((obj (cl-find-if + (lambda (obj) + (and (slot-exists-p obj 'argument) + (slot-boundp obj 'argument) + (equal (oref obj argument) arg))) + transient--suffixes))) (let ((transient--unset-incompatible nil)) (transient-infix-set obj nil))))) (cl-call-next-method obj value)))) @@ -3253,6 +3256,8 @@ have a history of their own.") (with-current-buffer buf (when transient-enable-popup-navigation (setq focus (or (button-get (point) 'command) + (and (not (bobp)) + (button-get (1- (point)) 'command)) (transient--heading-at-point)))) (erase-buffer) (setq window-size-fixed t) @@ -3384,7 +3389,9 @@ have a history of their own.") (insert ?\n) (insert (propertize " " 'display `(space :align-to (,(nth (1+ c) cc))))))) - (insert (make-string (max 1 (- (nth c cc) (current-column))) ?\s)) + (when (> c 0) + (insert (make-string (max 1 (- (nth c cc) (current-column))) + ?\s))) (when-let ((cell (nth r (nth c columns)))) (insert cell)) (when (= c (1- cs)) diff --git a/lisp/treesit.el b/lisp/treesit.el index cc7ec977851..ea701ce1ff7 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -3033,8 +3033,9 @@ See `treesit-language-source-alist' for details." Interactively, if `treesit-language-source-alist' doesn't already have data for building the grammar for LANG, prompt for its -repository URL and the C/C++ compiler to use. Non-interactively, -signal an error when there's no recipe for LANG. +repository URL and the C/C++ compiler to use. The recipe built +by the prompts are saved for the current session if the +installation is successful and the grammar is loadable. This command requires Git, a C compiler and (sometimes) a C++ compiler, and the linker to be installed and on PATH. It also requires that the @@ -3071,26 +3072,31 @@ nil, the grammar is installed to the standard location, the default-out-dir) out-dir))) (condition-case err - (apply #'treesit--install-language-grammar-1 - (cons out-dir recipe)) + (progn + (apply #'treesit--install-language-grammar-1 + ;; The nil is OUT-DIR. + (cons nil recipe)) + + ;; Check that the installed language grammar is loadable. + (pcase-let ((`(,available . ,err) + (treesit-language-available-p lang t))) + (if (not available) + (display-warning + 'treesit + (format "The installed language grammar for %s cannot be located or has problems (%s): %s" + lang (nth 0 err) + (string-join + (mapcar (lambda (x) (format "%s" x)) + (cdr err)) + " "))) + ;; If success, Save the recipe for the current session. + (setf (alist-get lang treesit-language-source-alist) + recipe)))) (error (display-warning 'treesit (format "Error encountered when installing language grammar: %s" - err))))) - - ;; Check that the installed language grammar is loadable. - (pcase-let ((`(,available . ,err) - (treesit-language-available-p lang t))) - (when (not available) - (display-warning - 'treesit - (format "The installed language grammar for %s cannot be located or has problems (%s): %s" - lang (nth 0 err) - (string-join - (mapcar (lambda (x) (format "%s" x)) - (cdr err)) - " ")))))) + err)))))) (defun treesit--call-process-signal (&rest args) "Run `call-process' with ARGS. diff --git a/lisp/use-package/use-package-core.el b/lisp/use-package/use-package-core.el index 0d99e270a3f..e0e16134ed3 100644 --- a/lisp/use-package/use-package-core.el +++ b/lisp/use-package/use-package-core.el @@ -1619,7 +1619,8 @@ Also see the Info node `(use-package) Creating an extension'." ;; See `use-package-handler/:ensure' for an explanation. (if (bound-and-true-p byte-compile-current-file) (funcall #'use-package-vc-install arg local-path) ; compile time - (push `(use-package-vc-install ',arg ,local-path) body)))) ; runtime + (push `(use-package-vc-install ',arg ,local-path) body)) ; runtime + body)) (defun use-package-normalize--vc-arg (arg) "Normalize possible arguments to the `:vc' keyword. diff --git a/lisp/wdired.el b/lisp/wdired.el index 9952da71078..5c745cc9aab 100644 --- a/lisp/wdired.el +++ b/lisp/wdired.el @@ -470,9 +470,8 @@ non-nil means return old filename." (insert wdired--old-content) (goto-char wdired--old-point)) (wdired-change-to-dired-mode) - ;; Make sure the display is in synch, and all the variables are set - ;; correctly. - (dired-revert) + ;; Update markers in `dired-subdir-alist' + (dired-build-subdir-alist) (set-buffer-modified-p nil) (setq buffer-undo-list nil) (message "Changes aborted")) diff --git a/src/lread.c b/src/lread.c index 43fa23003ca..eb52329f5a8 100644 --- a/src/lread.c +++ b/src/lread.c @@ -3647,7 +3647,7 @@ read_bool_vector (Lisp_Object readcharfun) /* Skip (and optionally remember) a lazily-loaded string preceded by "#@". Return true if this was a normal skip, - false if we read #@00 (which skips to EOB). */ + false if we read #@00 (which skips to EOB/EOF). */ static bool skip_lazy_string (Lisp_Object readcharfun) { @@ -4184,7 +4184,7 @@ read0 (Lisp_Object readcharfun, bool locate_syms) and function definitions that can be loaded lazily. */ if (skip_lazy_string (readcharfun)) goto read_obj; - obj = Qnil; /* #@00 skips to EOB and yields nil. */ + obj = Qnil; /* #@00 skips to EOB/EOF and yields nil. */ break; case '$': diff --git a/test/infra/Dockerfile.emba b/test/infra/Dockerfile.emba index 1969afdd333..21b69dacacc 100644 --- a/test/infra/Dockerfile.emba +++ b/test/infra/Dockerfile.emba @@ -29,7 +29,7 @@ FROM debian:bullseye as emacs-base RUN apt-get update && \ apt-get install -y --no-install-recommends -o=Dpkg::Use-Pty=0 \ libc-dev gcc g++ make autoconf automake libncurses-dev gnutls-dev \ - libdbus-1-dev libacl1-dev acl git texinfo gdb \ + libdbus-1-dev libacl1-dev acl git texinfo gawk gdb \ && rm -rf /var/lib/apt/lists/* FROM emacs-base as emacs-inotify diff --git a/test/infra/Makefile.in b/test/infra/Makefile.in index 5d40698541d..1af13a0096a 100644 --- a/test/infra/Makefile.in +++ b/test/infra/Makefile.in @@ -100,11 +100,20 @@ endef $(foreach subdir, $(SUBDIRS), $(eval $(call subdir_template,$(subdir)))) +TREE-SITTER-FILES ?= $(shell cd .. ; find lisp -name "*-ts-mode-tests.el" | sort | sed s/\\.el/.log/) + all: generate-test-jobs -.PHONY: generate-test-jobs $(FILE) $(SUBDIR_TARGETS) +.PHONY: generate-test-jobs $(FILE) $(SUBDIR_TARGETS) tree-sitter-files-template -generate-test-jobs: $(FILE) $(SUBDIR_TARGETS) +generate-test-jobs: $(FILE) $(SUBDIR_TARGETS) tree-sitter-files-template + +tree-sitter-files-template: + @echo >>$(FILE) + @echo '.tree-sitter-files-template:' >>$(FILE) + @echo ' variables:' >>$(FILE) + @echo ' tree_sitter_files: >-' >>$(FILE) + @for name in $(TREE-SITTER-FILES) ; do echo " $${name}" >>$(FILE) ; done $(FILE): $(AM_V_GEN) diff --git a/test/infra/gitlab-ci.yml b/test/infra/gitlab-ci.yml index ce2a92620fb..d5b18674c70 100644 --- a/test/infra/gitlab-ci.yml +++ b/test/infra/gitlab-ci.yml @@ -58,7 +58,7 @@ variables: # across multiple builds. BUILD_TAG: ${CI_COMMIT_REF_SLUG} # Disable if you don't need it, it can be a security risk. - CI_DEBUG_TRACE: "true" + # CI_DEBUG_TRACE: "true" default: image: docker:19.03.12 @@ -265,7 +265,14 @@ test-eglot: variables: target: emacs-eglot # This is needed in order to get a JUnit test report. - make_params: '-k -C test check-expensive LOGFILES="lisp/progmodes/eglot-tests.log"' + make_params: >- + '-k -C test check-expensive + LOGFILES="lisp/progmodes/eglot-tests.log" + TEST_HOME="/tmp" + EMACS_EXTRAOPT="--eval \(package-reinstall\ \(quote\ company\)\) + --eval \(package-reinstall\ \(quote\ yasnippet\)\) + --eval \(use-package\ company\) + --eval \(use-package\ yasnippet\)"' build-image-tree-sitter: stage: platform-images @@ -275,22 +282,14 @@ build-image-tree-sitter: test-tree-sitter: stage: platforms - extends: [.job-template, .test-template, .tree-sitter-template] + extends: [.job-template, .test-template, .tree-sitter-template, .tree-sitter-files-template] needs: - job: build-image-tree-sitter optional: true variables: target: emacs-tree-sitter # This is needed in order to get a JUnit test report. - files: >- - lisp/progmodes/c-ts-mode-tests.log - lisp/progmodes/elixir-ts-mode-tests.log - lisp/progmodes/go-ts-mode-tests.log - lisp/progmodes/heex-ts-mode-tests.log - lisp/progmodes/java-ts-mode-tests.log - lisp/progmodes/ruby-ts-mode-tests.log - lisp/progmodes/typescript-ts-mode-tests.log - make_params: '-k -C test check-expensive LD_LIBRARY_PATH=/usr/local/lib/tree-sitter LOGFILES="$files"' + make_params: '-k -C test check-expensive LD_LIBRARY_PATH=/usr/local/lib/tree-sitter LOGFILES="$tree_sitter_files"' build-image-gnustep: stage: platform-images diff --git a/test/infra/test-jobs.yml b/test/infra/test-jobs.yml index 4e575d50e9a..21c19c3043e 100644 --- a/test/infra/test-jobs.yml +++ b/test/infra/test-jobs.yml @@ -567,3 +567,14 @@ test-src-inotify: variables: target: emacs-inotify make_params: "-k -C test check-src" + +.tree-sitter-files-template: + variables: + tree_sitter_files: >- + lisp/progmodes/c-ts-mode-tests.log + lisp/progmodes/elixir-ts-mode-tests.log + lisp/progmodes/go-ts-mode-tests.log + lisp/progmodes/heex-ts-mode-tests.log + lisp/progmodes/java-ts-mode-tests.log + lisp/progmodes/ruby-ts-mode-tests.log + lisp/progmodes/typescript-ts-mode-tests.log diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el index b916232c4be..9323f72f384 100644 --- a/test/lisp/progmodes/python-tests.el +++ b/test/lisp/progmodes/python-tests.el @@ -136,20 +136,6 @@ STRING, it is skipped so the next STRING occurrence is selected." while pos collect (cons pos (get-text-property pos 'face)))) -(defun python-tests-assert-faces-after-change (content faces search replace) - "Assert that font faces for CONTENT are equal to FACES after change. -All occurrences of SEARCH are changed to REPLACE." - (python-tests-with-temp-buffer - content - ;; Force enable font-lock mode without jit-lock. - (rename-buffer "*python-font-lock-test*" t) - (let (noninteractive font-lock-support-mode) - (font-lock-mode)) - (while - (re-search-forward search nil t) - (replace-match replace)) - (should (equal faces (python-tests-get-buffer-faces))))) - (defun python-tests-self-insert (char-or-str) "Call `self-insert-command' for chars in CHAR-OR-STR." (let ((chars @@ -297,13 +283,6 @@ p = (1 + 2) "def 1func():" '((1 . font-lock-keyword-face) (4)))) -(ert-deftest python-font-lock-keywords-level-1-3 () - (python-tests-assert-faces - "def \\ - func():" - '((1 . font-lock-keyword-face) (4) - (15 . font-lock-function-name-face) (19)))) - (ert-deftest python-font-lock-assignment-statement-1 () (python-tests-assert-faces "a, b, c = 1, 2, 3" @@ -495,129 +474,6 @@ def f(x: CustomInt) -> CustomInt: (136 . font-lock-operator-face) (137) (144 . font-lock-keyword-face) (150)))) -(ert-deftest python-font-lock-assignment-statement-multiline-1 () - (python-tests-assert-faces-after-change - " -[ - a, - b -] # ( - 1, - 2 -) -" - '((1) - (8 . font-lock-variable-name-face) (9) - (15 . font-lock-variable-name-face) (16) - (19 . font-lock-operator-face) (20)) - "#" "=")) - -(ert-deftest python-font-lock-assignment-statement-multiline-2 () - (python-tests-assert-faces-after-change - " -[ - *a -] # 5, 6 -" - '((1) - (8 . font-lock-operator-face) - (9 . font-lock-variable-name-face) (10) - (13 . font-lock-operator-face) (14)) - "#" "=")) - -(ert-deftest python-font-lock-assignment-statement-multiline-3 () - (python-tests-assert-faces-after-change - "a\\ - ,\\ - b\\ - ,\\ - c\\ - #\\ - 1\\ - ,\\ - 2\\ - ,\\ - 3" - '((1 . font-lock-variable-name-face) (2) - (15 . font-lock-variable-name-face) (16) - (29 . font-lock-variable-name-face) (30) - (36 . font-lock-operator-face) (37)) - "#" "=")) - -(ert-deftest python-font-lock-assignment-statement-multiline-4 () - (python-tests-assert-faces-after-change - "a\\ - :\\ - int\\ - #\\ - 5" - '((1 . font-lock-variable-name-face) (2) - (15 . font-lock-builtin-face) (18) - (24 . font-lock-operator-face) (25)) - "#" "=")) - -(ert-deftest python-font-lock-assignment-statement-multiline-5 () - (python-tests-assert-faces-after-change - "(\\ - a\\ -)\\ - #\\ - 5\\ - ;\\ - (\\ - b\\ - )\\ - #\\ - 6" - '((1) - (8 . font-lock-variable-name-face) (9) - (18 . font-lock-operator-face) (19) - (46 . font-lock-variable-name-face) (47) - (60 . font-lock-operator-face) (61)) - "#" "=")) - -(ert-deftest python-font-lock-assignment-statement-multiline-6 () - (python-tests-assert-faces-after-change - "( - a -)\\ - #\\ - 5\\ - ;\\ - ( - b - )\\ - #\\ - 6" - '((1) - (7 . font-lock-variable-name-face) (8) - (16 . font-lock-operator-face) (17) - (43 . font-lock-variable-name-face) (44) - (56 . font-lock-operator-face) (57)) - "#" "=")) - -(ert-deftest python-font-lock-operator-1 () - (python-tests-assert-faces - "1 << 2 ** 3 == +4%-5|~6&7^8%9" - '((1) - (3 . font-lock-operator-face) (5) - (8 . font-lock-operator-face) (10) - (13 . font-lock-operator-face) (15) - (16 . font-lock-operator-face) (17) - (18 . font-lock-operator-face) (20) - (21 . font-lock-operator-face) (23) - (24 . font-lock-operator-face) (25) - (26 . font-lock-operator-face) (27) - (28 . font-lock-operator-face) (29)))) - -(ert-deftest python-font-lock-operator-2 () - "Keyword operators are font-locked as keywords." - (python-tests-assert-faces - "is_ is None" - '((1) - (5 . font-lock-keyword-face) (7) - (8 . font-lock-constant-face)))) - (ert-deftest python-font-lock-escape-sequence-string-newline () (python-tests-assert-faces "'\\n' diff --git a/test/lisp/use-package/use-package-tests.el b/test/lisp/use-package/use-package-tests.el index c8c20fc51cb..9181a8171a7 100644 --- a/test/lisp/use-package/use-package-tests.el +++ b/test/lisp/use-package/use-package-tests.el @@ -1991,6 +1991,17 @@ (use-package-vc-install '(other-name) ,load-path?) (require 'foo nil nil))))) +(ert-deftest use-package-test-handler/:vc-6 () + (let ((byte-compile-current-file "use-package-core.el") + tried-to-install) + (cl-letf (((symbol-function #'use-package-vc-install) + (lambda (arg &optional local-path) + (setq tried-to-install arg)))) + (should (equal + (use-package-handler/:vc 'foo nil 'some-pkg '(:init (foo)) nil) + '(foo))) + (should (eq tried-to-install 'some-pkg))))) + (ert-deftest use-package-test-normalize/:vc () (should (equal '(foo "version-string") (use-package-normalize/:vc 'foo :vc '("version-string"))))