Repository: remotemobprogramming/mob
Pull request #291 (2022-06-30 20:46:11)

Various fixes in suggestion of next typist
- Show list of last committers only if there are any
- Skip suggestions if there has only been a single person so far
- Consider the case of a new typist joining the session
- Fix reporting on first commit
- Rename mobber to typist



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6eca839..b5790fb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
 - Add a more specific error message if `git` is not installed.
 - Fix: `mob done --squash-wip` now successfully auto-merges auto-mergeable diverging changes.
 - Print the help output whenever any kind of help argument (`help`, `--help`, `-h`) is present in the command, e.g. `mob s 10 -h`.
+- Various fixes in suggestion of next typist:
+  - Show list of last committers only if there are any.
+  - Skip suggestions if there has only been a single person so far.
+  - Consider the case of a new typist joining the session.
+  - Fix reporting on first commit.
 
 # 3.1.4
 - Fixes a bug where mob saves the wrong filepath of the last modfied file in the wip commit message.
diff --git a/find_next.go b/find_next.go
index 534297e..74dda5d 100644
--- a/find_next.go
+++ b/find_next.go
@@ -1,16 +1,18 @@
 package main
 
 func findNextTypist(lastCommitters []string, gitUserName string) (nextTypist string, previousCommitters []string) {
+	nextTypistNeverDifferentFromGitUser := true
 	numberOfLastCommitters := len(lastCommitters)
 	for i := 0; i < numberOfLastCommitters; i++ {
 		if lastCommitters[i] == gitUserName && i > 0 {
 			nextTypist = lastCommitters[i-1]
 			if nextTypist != gitUserName {
+				nextTypistNeverDifferentFromGitUser = false
 				// '2*i+1' defines how far we look ahead. It is the number of already processed elements.
 				lookaheadThreshold := min(2*i+1, len(lastCommitters))
-				previousMobber := lookahead(lastCommitters[:i], lastCommitters[i:lookaheadThreshold])
-				if previousMobber != "" {
-					nextTypist = previousMobber
+				previousTypist := lookahead(lastCommitters[:i], lastCommitters[i:lookaheadThreshold])
+				if previousTypist != "" {
+					nextTypist = previousTypist
 				}
 				return
 			}
@@ -20,7 +22,30 @@ func findNextTypist(lastCommitters []string, gitUserName string) (nextTypist str
 			previousCommitters = prepend(previousCommitters, lastCommitters[i])
 		}
 	}
-	return
+	if nextTypist == "" {
+		// Current committer is new to the session.
+		numberOfPreviousCommitters := len(previousCommitters)
+		if numberOfPreviousCommitters == 2 {
+			nextTypist = previousCommitters[0]
+		} else if numberOfPreviousCommitters > 2 {
+			// Pick the next typist from the list of previous committers only.
+			reversedPreviousCommitters := reverse(previousCommitters[:len(previousCommitters)-1])
+			nextTypist, _ = findNextTypist(reversedPreviousCommitters, reversedPreviousCommitters[0])
+		}
+	} else if nextTypistNeverDifferentFromGitUser {
+		// Someone mobs themselves. ;)
+		nextTypist = ""
+	}
+	return nextTypist, nil
+}
+
+func reverse(list []string) []string {
+	length := len(list)
+	reversed := make([]string, length)
+	for i := 0; i < length; i++ {
+		reversed[length-1-i] = list[i]
+	}
+	return reversed
 }
 
 func lookahead(processedCommitters []string, previousCommitters []string) (nextTypist string) {
diff --git a/mob.go b/mob.go
index 67869f1..39f928e 100644
--- a/mob.go
+++ b/mob.go
@@ -1608,7 +1608,9 @@ func showNext(configuration Configuration) {
 	}
 	nextTypist, previousCommitters := findNextTypist(lines, gitUserName)
 	if nextTypist != "" {
-		sayInfo("Committers after your last commit: " + strings.Join(previousCommitters, ", "))
+		if len(previousCommitters) != 0 {
+			sayInfo("Committers after your last commit: " + strings.Join(previousCommitters, ", "))
+		}
 		sayInfo("***" + nextTypist + "*** is (probably) next.")
 	}
 }


Test patch:
diff --git a/find_next_test.go b/find_next_test.go
index 429465c..f71a97d 100644
--- a/find_next_test.go
+++ b/find_next_test.go
@@ -13,13 +13,31 @@ func TestFindNextTypistNoCommits(t *testing.T) {
 	equals(t, history, []string(nil))
 }
 
+func TestFindNextTypistOnFirstCommit(t *testing.T) {
+	lastCommitters := []string{"alice"}
+
+	nextTypist, history := findNextTypist(lastCommitters, "alice")
+
+	equals(t, nextTypist, "")
+	equals(t, history, []string(nil))
+}
+
+func TestFindNextTypistStartingWithFirstCommitterTwice(t *testing.T) {
+	lastCommitters := []string{"alice", "alice"}
+
+	nextTypist, history := findNextTypist(lastCommitters, "alice")
+
+	equals(t, nextTypist, "")
+	equals(t, history, []string(nil))
+}
+
 func TestFindNextTypistOnlyCurrentCommitterInList(t *testing.T) {
 	lastCommitters := []string{"alice", "alice", "alice"}
 
 	nextTypist, history := findNextTypist(lastCommitters, "alice")
 
-	equals(t, nextTypist, "alice")
-	equals(t, history, []string{"alice"})
+	equals(t, nextTypist, "")
+	equals(t, history, []string(nil))
 }
 
 func TestFindNextTypistCurrentCommitterAlternatingWithOneOtherPerson(t *testing.T) {
@@ -31,6 +49,24 @@ func TestFindNextTypistCurrentCommitterAlternatingWithOneOtherPerson(t *testing.
 	equals(t, history, []string{"bob", "alice"})
 }
 
+func TestFindNextTypistCommitterFirstSeenInFirstRound(t *testing.T) {
+	lastCommitters := []string{"alice", "bob", "craig"}
+
+	nextTypist, history := findNextTypist(lastCommitters, "alice")
+
+	equals(t, nextTypist, "craig")
+	equals(t, history, []string(nil))
+}
+
+func TestFindNextTypistSecondCommitterFirstSeenRunningSession(t *testing.T) {
+	lastCommitters := []string{"alice", "bob", "craig", "bob"}
+
+	nextTypist, history := findNextTypist(lastCommitters, "alice")
+
+	equals(t, nextTypist, "craig")
+	equals(t, history, []string(nil))
+}
+
 func TestFindNextTypistCurrentCommitterCommittedBefore(t *testing.T) {
 	lastCommitters := []string{"alice", "alice", "bob", "alice"}