@@ -103,7 +103,7 @@ public static Source ParseSource(this Status status)
103103 return ParseSource ( status . Source ) ;
104104 }
105105
106- private static List < DoubleUtf16Char > EnumerateChars ( string str )
106+ private static List < DoubleUtf16Char > GetCodePoints ( string str )
107107 {
108108 var result = new List < DoubleUtf16Char > ( str . Length ) ;
109109 for ( var i = 0 ; i < str . Length ; i ++ )
@@ -116,21 +116,6 @@ private static List<DoubleUtf16Char> EnumerateChars(string str)
116116 return result ;
117117 }
118118
119- private static string ToString ( IList < DoubleUtf16Char > source , int start )
120- {
121- var sourceLen = source . Count ;
122- var arr = new char [ sourceLen * 2 ] ;
123- var strLen = 0 ;
124- for ( var i = start ; i < sourceLen ; i ++ )
125- {
126- var x = source [ i ] ;
127- arr [ strLen ++ ] = x . X ;
128- if ( char . IsHighSurrogate ( x . X ) )
129- arr [ strLen ++ ] = x . Y ;
130- }
131- return new string ( arr , 0 , strLen ) ;
132- }
133-
134119 private static string ToString ( IList < DoubleUtf16Char > source , int start , int count )
135120 {
136121 var arr = new char [ count * 2 ] ;
@@ -154,8 +139,34 @@ private static string ToString(IList<DoubleUtf16Char> source, int start, int cou
154139 /// <returns>An <see cref="IEnumerable{ITextPart}"/> whose elements are parts of <paramref name="text"/>.</returns>
155140 public static IEnumerable < TextPart > EnumerateTextParts ( string text , Entities entities )
156141 {
142+ var chars = GetCodePoints ( text ) ;
143+ return EnumerateTextParts ( chars , entities , 0 , chars . Count ) ;
144+ }
145+
146+ /// <summary>
147+ /// Enumerates parts split into Tweet Entities.
148+ /// </summary>
149+ /// <param name="text">The text such as <see cref="Status.Text"/>, <see cref="DirectMessage.Text"/> and <see cref="User.Description"/>.</param>
150+ /// <param name="entities">The <see cref="Entities"/> instance.</param>
151+ /// <param name="startIndex">The starting character position in code point.</param>
152+ /// <param name="endIndex">The ending character position in code point.</param>
153+ /// <returns>An <see cref="IEnumerable{ITextPart}"/> whose elements are parts of <paramref name="text"/>.</returns>
154+ public static IEnumerable < TextPart > EnumerateTextParts ( string text , Entities entities , int startIndex , int endIndex )
155+ {
156+ return EnumerateTextParts ( GetCodePoints ( text ) , entities , startIndex , endIndex ) ;
157+ }
158+
159+ private static IEnumerable < TextPart > EnumerateTextParts ( IList < DoubleUtf16Char > chars , Entities entities , int startIndex , int endIndex )
160+ {
161+ if ( startIndex < 0 || startIndex >= chars . Count )
162+ throw new ArgumentOutOfRangeException ( nameof ( startIndex ) ) ;
163+
164+ if ( endIndex < startIndex || endIndex > chars . Count )
165+ throw new ArgumentOutOfRangeException ( nameof ( endIndex ) ) ;
166+
157167 if ( entities == null )
158168 {
169+ var text = ToString ( chars , startIndex , endIndex - startIndex ) ;
159170 yield return new TextPart ( )
160171 {
161172 RawText = text ,
@@ -202,7 +213,7 @@ public static IEnumerable<TextPart> EnumerateTextParts(string text, Entities ent
202213 Type = TextPartType . Url ,
203214 Start = e . Indices [ 0 ] ,
204215 End = e . Indices [ 1 ] ,
205- RawText = e . Url . ToString ( ) ,
216+ RawText = e . Url ,
206217 Text = e . DisplayUrl ,
207218 Entity = e
208219 } )
@@ -219,11 +230,13 @@ public static IEnumerable<TextPart> EnumerateTextParts(string text, Entities ent
219230 Entity = e
220231 } )
221232 )
233+ . Where ( e => e . Start >= startIndex && e . Start < endIndex )
222234 . OrderBy ( part => part . Start )
223235 ) ;
224236
225237 if ( list . Count == 0 )
226238 {
239+ var text = ToString ( chars , startIndex , endIndex - startIndex ) ;
227240 yield return new TextPart ( )
228241 {
229242 RawText = text ,
@@ -233,11 +246,10 @@ public static IEnumerable<TextPart> EnumerateTextParts(string text, Entities ent
233246 }
234247
235248 var current = list . First ;
236- var chars = EnumerateChars ( text ) ;
237249
238250 while ( true )
239251 {
240- var start = current . Previous ? . Value . End ?? 0 ;
252+ var start = current . Previous ? . Value . End ?? startIndex ;
241253 var count = current . Value . Start - start ;
242254 if ( count > 0 )
243255 {
@@ -256,9 +268,9 @@ public static IEnumerable<TextPart> EnumerateTextParts(string text, Entities ent
256268 }
257269
258270 var lastStart = current . Value . End ;
259- if ( lastStart < chars . Count )
271+ if ( lastStart < endIndex )
260272 {
261- var lastOutput = ToString ( chars , lastStart ) ;
273+ var lastOutput = ToString ( chars , lastStart , endIndex - lastStart ) ;
262274 yield return new TextPart ( )
263275 {
264276 RawText = lastOutput ,
@@ -287,6 +299,38 @@ public static IEnumerable<TextPart> EnumerateTextParts(this DirectMessage dm)
287299 return EnumerateTextParts ( dm . Text , dm . Entities ) ;
288300 }
289301
302+ /// <summary>
303+ /// Enumerates parts split into Tweet Entities.
304+ /// </summary>
305+ /// <param name="status">The <see cref="Status"/> instance.</param>
306+ /// <returns>An <see cref="ExtendedTweetInfo"/> instance.</returns>
307+ public static ExtendedTweetInfo GetExtendedTweetElements ( this Status status )
308+ {
309+ var displayTextRange = status . DisplayTextRange ?? status . ExtendedTweet ? . DisplayTextRange ;
310+ if ( displayTextRange == null )
311+ return new ExtendedTweetInfo
312+ {
313+ TweetText = status . EnumerateTextParts ( ) . ToArray ( ) ,
314+ HiddenPrefix = new UserMentionEntity [ 0 ] ,
315+ HiddenSuffix = new UrlEntity [ 0 ]
316+ } ;
317+
318+ var start = displayTextRange [ 0 ] ;
319+ var end = displayTextRange [ 1 ] ;
320+ return new ExtendedTweetInfo
321+ {
322+ TweetText = EnumerateTextParts (
323+ status . FullText ?? status . ExtendedTweet ? . FullText ?? status . Text ,
324+ status . Entities ,
325+ start , end ) . ToArray ( ) ,
326+ HiddenPrefix = status . Entities ? . UserMentions == null ? new UserMentionEntity [ 0 ]
327+ : status . Entities . UserMentions . Where ( x => x . Indices [ 0 ] < start ) . ToArray ( ) ,
328+ HiddenSuffix = ( status . Entities ? . Urls ?? Enumerable . Empty < UrlEntity > ( ) )
329+ . Concat ( status . Entities ? . Media ?? Enumerable . Empty < UrlEntity > ( ) )
330+ . Where ( x => x . Indices [ 0 ] >= end ) . ToArray ( )
331+ } ;
332+ }
333+
290334 /// <summary>
291335 /// Returns the URI suffix for given profile size image variant. See User Profile Images and Banners.
292336 /// </summary>
@@ -443,4 +487,25 @@ public DoubleUtf16Char(char x, char y)
443487 this . Y = y ;
444488 }
445489 }
490+
491+ /// <summary>
492+ /// Represents a Tweet rendered in Extended mode.
493+ /// </summary>
494+ public class ExtendedTweetInfo
495+ {
496+ /// <summary>
497+ /// The elements of Tweet Text part.
498+ /// </summary>
499+ public TextPart [ ] TweetText { get ; set ; }
500+
501+ /// <summary>
502+ /// Replies.
503+ /// </summary>
504+ public UserMentionEntity [ ] HiddenPrefix { get ; set ; }
505+
506+ /// <summary>
507+ /// Attachment URLs.
508+ /// </summary>
509+ public UrlEntity [ ] HiddenSuffix { get ; set ; }
510+ }
446511}
0 commit comments