@@ -948,15 +948,7 @@ class _SerializeVisitor
948948 case $gs:
949949 case $rs:
950950 case $us:
951- buffer.writeCharCode ($backslash);
952- if (char > 0xF ) buffer.writeCharCode (hexCharFor (char >> 4 ));
953- buffer.writeCharCode (hexCharFor (char & 0xF ));
954- if (string.length == i + 1 ) break ;
955-
956- var next = string.codeUnitAt (i + 1 );
957- if (isHex (next) || next == $space || next == $tab) {
958- buffer.writeCharCode ($space);
959- }
951+ _writeEscape (buffer, char, string, i);
960952 break ;
961953
962954 case $backslash:
@@ -965,6 +957,12 @@ class _SerializeVisitor
965957 break ;
966958
967959 default :
960+ var newIndex = _tryPrivateUseCharacter (buffer, char, string, i);
961+ if (newIndex != null ) {
962+ i = newIndex;
963+ break ;
964+ }
965+
968966 buffer.writeCharCode (char);
969967 break ;
970968 }
@@ -996,13 +994,66 @@ class _SerializeVisitor
996994 break ;
997995
998996 default :
999- _buffer.writeCharCode (char);
1000997 afterNewline = false ;
998+ var newIndex = _tryPrivateUseCharacter (_buffer, char, string, i);
999+ if (newIndex != null ) {
1000+ i = newIndex;
1001+ break ;
1002+ }
1003+
1004+ _buffer.writeCharCode (char);
10011005 break ;
10021006 }
10031007 }
10041008 }
10051009
1010+ /// If [codeUnit] is (the beginning of) a private-use character and Sass isn't
1011+ /// emitting compressed CSS, writes that character as an escape to [buffer] .
1012+ ///
1013+ /// The [string] is the string from which [codeUnit] was read, and [i] is the
1014+ /// index it was read from. If this successfully writes the character, returns
1015+ /// the index of the *last* code unit that was consumed for it. Otherwise,
1016+ /// returns `null` .
1017+ ///
1018+ /// In expanded mode, we print all characters in Private Use Areas as escape
1019+ /// codes since there's no useful way to render them directly. These
1020+ /// characters are often used for glyph fonts, where it's useful for readers
1021+ /// to be able to distinguish between them in the rendered stylesheet.
1022+ int ? _tryPrivateUseCharacter (
1023+ StringBuffer buffer, int codeUnit, String string, int i) {
1024+ if (_isCompressed) return null ;
1025+
1026+ if (isPrivateUseBMP (codeUnit)) {
1027+ _writeEscape (buffer, codeUnit, string, i);
1028+ return i;
1029+ }
1030+
1031+ if (isPrivateUseHighSurrogate (codeUnit) && string.length > i + 1 ) {
1032+ _writeEscape (buffer,
1033+ combineSurrogates (codeUnit, string.codeUnitAt (i + 1 )), string, i + 1 );
1034+ return i + 1 ;
1035+ }
1036+
1037+ return null ;
1038+ }
1039+
1040+ /// Writes [character] as a hexadecimal escape sequence to [buffer] .
1041+ ///
1042+ /// The [string] is the string from which the escape is being written, and [i]
1043+ /// is the index of the last code unit of [character] in that string. These
1044+ /// are used to write a trailing space after the escape if necessary to
1045+ /// disambiguate it from the next character.
1046+ void _writeEscape (StringBuffer buffer, int character, String string, int i) {
1047+ buffer.writeCharCode ($backslash);
1048+ buffer.write (character.toRadixString (16 ));
1049+
1050+ if (string.length == i + 1 ) return ;
1051+ var next = string.codeUnitAt (i + 1 );
1052+ if (isHex (next) || next == $space || next == $tab) {
1053+ buffer.writeCharCode ($space);
1054+ }
1055+ }
1056+
10061057 // ## Selectors
10071058
10081059 void visitAttributeSelector (AttributeSelector attribute) {
0 commit comments