Skip to content

Commit

Permalink
Linewrap PR description to 72 chars automatically (#923)
Browse files Browse the repository at this point in the history
## Summary
* Linewrap the PR description to 72 chars automatically when merging a
PR
* Codeblocks (```` ``` ````, `~~~` seperated or indented with 4 or more
spaces, see
https://github.github.com/gfm/#indented-code-block ) are not
linewrapped,
and codespans `like this` are treated as single words

## Details
* Remove the manual 72 char requirement and merge the top comment into
the comment below the section break
* Restrict the removal of html comment introduced in

a862810
to non-codeblocks

---------

Co-authored-by: Saem Ghani <saemghani+github@gmail.com>
  • Loading branch information
Clyybber and saem authored Sep 26, 2023
1 parent 11751e9 commit 4cd20fa
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 6 deletions.
7 changes: 2 additions & 5 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
<!--- The Pull Request (=PR) message is what will get automatically used as
the commit message when the PR is merged. Make sure that no line is longer
than 72 characters -->

## Summary
* what changed and how?
* why are we changing it?
Expand All @@ -14,7 +10,8 @@ than 72 characters -->
Fixes full_issue_url

---
<!-- Note: section break (`---`) onwards is not in CI merge commit -->
<!--- Anything before this section break (`---`) will be used as
the commit message when the PR is merged. -->

## Notes for Reviewers
* leave additional context for reviewers
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/slash-command-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ jobs:
name: Trim description and merge
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
tools/ci_format_pr_description.js
- id: pr-data
name: Get PR data and trim PR body
uses: actions/github-script@v6
Expand Down Expand Up @@ -51,8 +55,10 @@ jobs:
return;
}
const formatPR = require('../../tools/ci_format_pr_description.js');
let [ body, body_tail ] = pr_info.body.split(/^---\s*$/m, 2);
body = body.replace(/<\!--.*?-->/s, "");
body = formatPR.formatPRdescription(body, 72);
body = body.trimEnd();
body_tail = body_tail?.trimEnd() || '';
// Only runs when the PR is still open and is not staged for merging.
Expand Down
170 changes: 170 additions & 0 deletions tests/tools/tci_format_pr_description.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
discard """
description: "Test the PR description formatter"
joinable: false
target: js
"""

import std/jsffi

let formatPR = require "../../tools/ci_format_pr_description.js"

proc formatPRdescription(formatPR: JSObject, str: cstring, maxLineLength: int): cstring {.importjs.}

let testingText = cstring"""Lorem ipsum dolor sit amet, consectetur i adipiscing elit. Nullam blandit mauris id venenatis tincidunt. Vestibulum at gravida sapien. Mauris tellus augue, aliquet sed laoreet blandit, pulvinar sed felis. Phasellus nec est vitae enim blandit facilisis.
Vestibulum fermentum ligula sit amet volutpat fermentum. Sed in faucibus orci. Pellentesque a dui ex. Curabitur sollicitudin, nulla id dignissim lacinia, odio mauris blandit nisi, eget auctor arcu odio nec est.
=========================================
## Summary
* The quick brown fox now jumps over the lazy dog
## Details
* THE QUICK BROWN FOX JUMPED OVER THE LAZY DOG'S BACK 1234567890
* Hamburgevons
<!--This HTML comment should be removed-->
```
this is a triple quote delimited codeblock:
it should not be linewrapped as should no other style of codeblock be
there are also some birds sitting on a html comment:
___ ___
(o o) (o o)
( V ) ( V )
<!--------m-m-----m-m--------->
```
Here's some text.
And some indented text, that will be linewrapped,
but must not be dedented
<!--- This HTML comment
spanning multiple lines should be removed
too! -->
and this is an indented codeblock:
with a cat:
!"#%&'()*+,-./012 _._ _,-'""`-._ 3456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefgh
"#%&'()*+,-./0123 (,-.'._,'( |\`-/| 456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghi
#%&'()*+,-./01234 '-.-' \ )-`( , o o) 56789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghij
%&'()*+,-./012345 '- \`_`"'- 6789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijk
<!--and-a-html-comment-too--->
https://www.some.url/that/is/quite/long/abcdefghijklmnopqrstuvw123467890xyz
(test) =========================================
(test) ======================================= i
~~~tokipona
<!-This is important!-->
jan ali li kama lon nasin ni: ona li ken tawa li ken pali.
jan ali li kama lon sama. jan ali li jo e ken pi pilin suli.
jan ali li ken pali e wile pona ona.
jan ali li jo e ken pi sona pona e ken pi pali pona.
jan ali li wile pali nasin ni: ona li jan pona pi jan ante.
~~~
=================================== split this
================================= `keep this`
there are trailing spaces after this sentence
and they must not leak into this line
the next line is indented
and it should stay this way
"""

let testingTextWrapped = cstring"""Lorem ipsum dolor sit amet, consectetur i
adipiscing elit. Nullam blandit mauris id
venenatis tincidunt. Vestibulum at
gravida sapien. Mauris tellus augue,
aliquet sed laoreet blandit, pulvinar sed
felis. Phasellus nec est vitae enim
blandit facilisis.
Vestibulum fermentum ligula sit amet
volutpat fermentum. Sed in faucibus orci.
Pellentesque a dui ex. Curabitur
sollicitudin, nulla id dignissim lacinia,
odio mauris blandit nisi, eget auctor
arcu odio nec est.
=========================================
## Summary
* The quick brown fox now jumps over the
lazy dog
## Details
* THE QUICK BROWN FOX JUMPED OVER THE
LAZY DOG'S BACK 1234567890
* Hamburgevons
```
this is a triple quote delimited codeblock:
it should not be linewrapped as should no other style of codeblock be
there are also some birds sitting on a html comment:
___ ___
(o o) (o o)
( V ) ( V )
<!--------m-m-----m-m--------->
```
Here's some text.
And some indented text, that will be
linewrapped,
but must not be dedented
and this is an indented codeblock:
with a cat:
!"#%&'()*+,-./012 _._ _,-'""`-._ 3456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefgh
"#%&'()*+,-./0123 (,-.'._,'( |\`-/| 456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghi
#%&'()*+,-./01234 '-.-' \ )-`( , o o) 56789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghij
%&'()*+,-./012345 '- \`_`"'- 6789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijk
<!--and-a-html-comment-too--->
https://www.some.url/that/is/quite/long/abcdefghijklmnopqrstuvw123467890xyz
(test)
=========================================
(test)
======================================= i
~~~tokipona
<!-This is important!-->
jan ali li kama lon nasin ni: ona li ken tawa li ken pali.
jan ali li kama lon sama. jan ali li jo e ken pi pilin suli.
jan ali li ken pali e wile pona ona.
jan ali li jo e ken pi sona pona e ken pi pali pona.
jan ali li wile pali nasin ni: ona li jan pona pi jan ante.
~~~
=================================== split
this
=================================
`keep this`
there are trailing spaces after this
sentence
and they must not leak into this line
the next line is indented
and it should stay this way
"""

echo formatPR.formatPRdescription(testingText, 41)
assert formatPR.formatPRdescription(testingText, 41) == testingTextWrapped

53 changes: 53 additions & 0 deletions tools/ci_format_pr_description.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// This file is imported by .github/workflows/slash-command-merge.yml
// and tested by tests/tools/tci_forrmat_pr_description.nim

function getSegments(regex, str) {
// Returns the segments that `regex`
// divides `str` into, along with the
// info of wether each segment was matching
// the regex or not
let segments = [];
let lastIndex = 0;
let match;
regex.lastIndex = 0; // In case there's a dangling previous search
while (match = regex.exec(str)) {
if (match.index > lastIndex) {
segments.push([false, str.substring(lastIndex, match.index)]);
}
segments.push([true, match[0]]);
lastIndex = match.index + match[0].length;
}
if (lastIndex < str.length) {
segments.push([false, str.substring(lastIndex)]);
}
return segments;
}

exports.formatPRdescription = (text, maxLineLength) =>
// Returns `text` stripped of HTML comments and linewrapped,
// respecting codeblocks and codespans
getSegments(/(?:^ {0,3}(```|~~~)[^]*?^ {0,3}\1 *$)|^(?:\n {4,}.*)+/gm, text)
.map( ([isCodeBlock, segment]) =>
isCodeBlock
? segment // Don't touch code blocks
: segment
// Remove HTML comments first:
.replace(/<\!--.*?-->/s, "")
.split('\n').map(line =>
line.length <= maxLineLength
? line // Line isn't too long, nothing to do
: getSegments(/(`+).*?\1/g, line)
// Treat codespans as single words:
.flatMap( ([isCodeSpan, segment]) => isCodeSpan ? segment : segment.split(' ') )
.reduce( ([wrappedLine, lineLength, isStart], word) =>
// Don't touch indentation nor start with a line break:
isStart
? [wrappedLine + word, lineLength + word.length, false]
: // Don't touch trailing spaces:
word.length > 0 && lineLength + 1 + word.length > maxLineLength
? [wrappedLine + '\n' + word, word.length, false]
: [wrappedLine + ' ' + word, lineLength + 1 + word.length, false]
, ['', 0, true]
)[0]
).join('\n')
).join('')

0 comments on commit 4cd20fa

Please sign in to comment.