| 32 | * maxSummaryLength + 1 long when rendered. |
| 33 | */ |
| 34 | export function wrapRichTextCommitMessage( |
| 35 | summaryText: string, |
| 36 | bodyText: string, |
| 37 | tokenizer: Tokenizer, |
| 38 | maxSummaryLength = MaxSummaryLength |
| 39 | ): { summary: ReadonlyArray<TokenResult>; body: ReadonlyArray<TokenResult> } { |
| 40 | const tokens = tokenizer.tokenize(summaryText.trimRight()) |
| 41 | |
| 42 | const summary = new Array<TokenResult>() |
| 43 | const overflow = new Array<TokenResult>() |
| 44 | |
| 45 | let remainder = maxSummaryLength |
| 46 | |
| 47 | for (const token of tokens) { |
| 48 | // An emoji token like ":white_square_button: would still only take |
| 49 | // up a little bit more space than a regular character when rendered |
| 50 | // as an image, we take that into consideration here with an approximation |
| 51 | // that an emoji is twice as wide as a normal character. |
| 52 | const charCount = token.kind === TokenType.Emoji ? 2 : token.text.length |
| 53 | |
| 54 | if (remainder <= 0) { |
| 55 | // There's no room left in the summary, everything needs to |
| 56 | // go into the overflow |
| 57 | overflow.push(token) |
| 58 | } else if (remainder >= charCount) { |
| 59 | // The token fits without us having to think about wrapping! |
| 60 | summary.push(token) |
| 61 | remainder -= charCount |
| 62 | } else { |
| 63 | // There's not enough room to include the token in its entirety, |
| 64 | // we've got to make a decision between hard wrapping or pushing |
| 65 | // to overflow. |
| 66 | if (token.kind === TokenType.Text) { |
| 67 | // We always hard-wrap text, it'd be nice if we could attempt |
| 68 | // to break at word boundaries in the future but that's too |
| 69 | // complex for now. |
| 70 | summary.push(text(token.text.substring(0, remainder))) |
| 71 | overflow.push(text(token.text.substring(remainder))) |
| 72 | } else if (token.kind === TokenType.Emoji) { |
| 73 | // Can't hard-wrap inside an emoji |
| 74 | overflow.push(token) |
| 75 | } else if (token.kind === TokenType.Link) { |
| 76 | // Hard wrapping an issue link is confusing so we treat them |
| 77 | // as atomic. For all other links (@mentions or https://...) |
| 78 | // We want at least the first couple of characters of the link |
| 79 | // text showing otherwise we'll end up with weird links like "h" |
| 80 | // or "@" |
| 81 | if (!token.text.startsWith('#') && remainder > 5) { |
| 82 | summary.push(link(token.text.substring(0, remainder), token.text)) |
| 83 | overflow.push(link(token.text.substring(remainder), token.text)) |
| 84 | } else { |
| 85 | overflow.push(token) |
| 86 | } |
| 87 | } else { |
| 88 | return assertNever(token, `Unknown token type`) |
| 89 | } |
| 90 | |
| 91 | remainder = 0 |