refactor function diffText (#3615)
This commit is contained in:
parent
60b160997c
commit
9e7f1338f0
|
@ -139,10 +139,11 @@ type MentionState struct {
|
||||||
AtSignIdx int
|
AtSignIdx int
|
||||||
AtIdxs []*AtIndexEntry
|
AtIdxs []*AtIndexEntry
|
||||||
MentionEnd int
|
MentionEnd int
|
||||||
PreviousText string // searched text
|
PreviousText string
|
||||||
NewText *string // the matched username
|
NewText string
|
||||||
Start int // position after the @
|
Start int
|
||||||
End int // position of the end of newText
|
End int
|
||||||
|
operation textOperation
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *MentionState) String() string {
|
func (ms *MentionState) String() string {
|
||||||
|
@ -154,7 +155,7 @@ func (ms *MentionState) String() string {
|
||||||
atIdxsStr += fmt.Sprintf("%+v", entry)
|
atIdxsStr += fmt.Sprintf("%+v", entry)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("MentionState{AtSignIdx: %d, AtIdxs: [%s], MentionEnd: %d, PreviousText: %q, NewText: %s, Start: %d, End: %d}",
|
return fmt.Sprintf("MentionState{AtSignIdx: %d, AtIdxs: [%s], MentionEnd: %d, PreviousText: %q, NewText: %s, Start: %d, End: %d}",
|
||||||
ms.AtSignIdx, atIdxsStr, ms.MentionEnd, ms.PreviousText, *ms.NewText, ms.Start, ms.End)
|
ms.AtSignIdx, atIdxsStr, ms.MentionEnd, ms.PreviousText, ms.NewText, ms.Start, ms.End)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChatMentionContext struct {
|
type ChatMentionContext struct {
|
||||||
|
@ -206,7 +207,7 @@ func (m *MentionManager) getChatMentionContext(chatID string) *ChatMentionContex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MentionManager) getMentionableUser(chatID string, pk string) (*MentionableUser, error) {
|
func (m *MentionManager) getMentionableUser(chatID string, pk string) (*MentionableUser, error) {
|
||||||
mentionableUsers, err := m.getMentionableUsers(chatID)
|
mentionableUsers, err := m.mentionableUserGetter.getMentionableUsers(chatID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -283,7 +284,7 @@ func (m *MentionManager) ReplaceWithPublicKey(chatID, text string) (string, erro
|
||||||
if chat == nil {
|
if chat == nil {
|
||||||
return "", fmt.Errorf("chat not found when check mentions, chatID: %s", chatID)
|
return "", fmt.Errorf("chat not found when check mentions, chatID: %s", chatID)
|
||||||
}
|
}
|
||||||
mentionableUsers, err := m.getMentionableUsers(chatID)
|
mentionableUsers, err := m.mentionableUserGetter.getMentionableUsers(chatID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -292,28 +293,29 @@ func (m *MentionManager) ReplaceWithPublicKey(chatID, text string) (string, erro
|
||||||
return newText, nil
|
return newText, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MentionManager) OnChangeText(chatID, text string) (*ChatMentionContext, error) {
|
func (m *MentionManager) OnChangeText(chatID, fullText string) (*ChatMentionContext, error) {
|
||||||
ctx := m.getChatMentionContext(chatID)
|
ctx := m.getChatMentionContext(chatID)
|
||||||
diff := diffText(ctx.PreviousText, text)
|
diff := diffText(ctx.PreviousText, fullText)
|
||||||
if diff == nil {
|
if diff == nil {
|
||||||
return ctx, nil
|
return ctx, nil
|
||||||
}
|
}
|
||||||
ctx.PreviousText = text
|
ctx.PreviousText = fullText
|
||||||
if ctx.MentionState == nil {
|
if ctx.MentionState == nil {
|
||||||
ctx.MentionState = &MentionState{}
|
ctx.MentionState = &MentionState{}
|
||||||
}
|
}
|
||||||
ctx.MentionState.PreviousText = diff.previousText
|
ctx.MentionState.PreviousText = diff.previousText
|
||||||
ctx.MentionState.NewText = &diff.newText
|
ctx.MentionState.NewText = diff.newText
|
||||||
ctx.MentionState.Start = diff.start
|
ctx.MentionState.Start = diff.start
|
||||||
ctx.MentionState.End = diff.end
|
ctx.MentionState.End = diff.end
|
||||||
|
ctx.MentionState.operation = diff.operation
|
||||||
|
|
||||||
ctx.MentionState.AtIdxs = calcAtIdxs(ctx.MentionState)
|
ctx.MentionState.AtIdxs = calcAtIdxs(ctx.MentionState)
|
||||||
m.logger.Debug("OnChangeText", zap.String("chatID", chatID), zap.Any("state", ctx.MentionState))
|
m.logger.Debug("OnChangeText", zap.String("chatID", chatID), zap.Any("state", ctx.MentionState))
|
||||||
return m.CalculateSuggestions(chatID, text)
|
return m.calculateSuggestions(chatID, fullText)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MentionManager) recheckAtIdxs(chatID string, text string, publicKey string) (*ChatMentionContext, error) {
|
func (m *MentionManager) recheckAtIdxs(chatID string, text string, publicKey string) (*ChatMentionContext, error) {
|
||||||
user, err := m.getMentionableUser(chatID, publicKey)
|
user, err := m.mentionableUserGetter.getMentionableUser(chatID, publicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -326,58 +328,61 @@ func (m *MentionManager) recheckAtIdxs(chatID string, text string, publicKey str
|
||||||
return ctx, nil
|
return ctx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MentionManager) CalculateSuggestions(chatID, text string) (*ChatMentionContext, error) {
|
func (m *MentionManager) calculateSuggestions(chatID, fullText string) (*ChatMentionContext, error) {
|
||||||
ctx := m.getChatMentionContext(chatID)
|
ctx := m.getChatMentionContext(chatID)
|
||||||
|
|
||||||
mentionableUsers, err := m.mentionableUserGetter.getMentionableUsers(chatID)
|
mentionableUsers, err := m.mentionableUserGetter.getMentionableUsers(chatID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
m.logger.Debug("CalculateSuggestions", zap.String("chatID", chatID), zap.String("text", text), zap.Int("num of mentionable user", len(mentionableUsers)))
|
m.logger.Debug("calculateSuggestions", zap.String("chatID", chatID), zap.String("fullText", fullText), zap.Int("num of mentionable user", len(mentionableUsers)))
|
||||||
|
|
||||||
m.calculateSuggestions(chatID, text, mentionableUsers)
|
m.calculateSuggestionsWithMentionableUsers(chatID, fullText, mentionableUsers)
|
||||||
|
|
||||||
return ctx, nil
|
return ctx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MentionManager) calculateSuggestions(chatID string, text string, mentionableUsers map[string]*MentionableUser) {
|
func (m *MentionManager) calculateSuggestionsWithMentionableUsers(chatID string, fullText string, mentionableUsers map[string]*MentionableUser) {
|
||||||
ctx := m.getChatMentionContext(chatID)
|
ctx := m.getChatMentionContext(chatID)
|
||||||
state := ctx.MentionState
|
state := ctx.MentionState
|
||||||
newText := state.NewText
|
|
||||||
if newText == nil {
|
|
||||||
newText = &text
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(state.AtIdxs) == 0 {
|
if len(state.AtIdxs) == 0 {
|
||||||
state.AtIdxs = nil
|
state.AtIdxs = nil
|
||||||
ctx.MentionSuggestions = nil
|
ctx.MentionSuggestions = nil
|
||||||
ctx.InputSegments = []InputSegment{{
|
ctx.InputSegments = []InputSegment{{
|
||||||
Type: Text,
|
Type: Text,
|
||||||
Value: text,
|
Value: fullText,
|
||||||
}}
|
}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
newAtIdxs := checkIdxForMentions(text, state.AtIdxs, mentionableUsers)
|
newAtIndexEntries := checkIdxForMentions(fullText, state.AtIdxs, mentionableUsers)
|
||||||
calculatedInput := calculateInput(text, newAtIdxs)
|
calculatedInput := calculateInput(fullText, newAtIndexEntries)
|
||||||
addition := state.Start <= state.End
|
|
||||||
var end int
|
|
||||||
if addition {
|
|
||||||
end = state.Start + len([]rune(*newText))
|
|
||||||
} else {
|
|
||||||
end = state.Start
|
|
||||||
}
|
|
||||||
atSignIdx := lastIndexOf(text, charAtSign, state.End)
|
|
||||||
searchedText := strings.ToLower(subs(text, atSignIdx+1, end))
|
|
||||||
m.logger.Debug("calculateSuggestions", zap.Int("atSignIdx", atSignIdx), zap.String("searchedText", searchedText), zap.String("text", text), zap.Any("state", state))
|
|
||||||
|
|
||||||
|
var end int
|
||||||
|
switch state.operation {
|
||||||
|
case textOperationAdd:
|
||||||
|
end = state.Start + len([]rune(state.NewText))
|
||||||
|
case textOperationDelete:
|
||||||
|
end = state.Start
|
||||||
|
case textOperationReplace:
|
||||||
|
end = state.Start + len([]rune(state.NewText))
|
||||||
|
default:
|
||||||
|
m.logger.Error("calculateSuggestionsWithMentionableUsers: unknown textOperation", zap.String("chatID", chatID), zap.String("fullText", fullText), zap.Any("state", state))
|
||||||
|
}
|
||||||
|
|
||||||
|
atSignIdx := lastIndexOf(fullText, charAtSign, end)
|
||||||
var suggestions map[string]*MentionableUser
|
var suggestions map[string]*MentionableUser
|
||||||
if (atSignIdx <= state.Start && end-atSignIdx <= 100) || text[len(text)-1] == charAtSign[0] {
|
if atSignIdx != -1 {
|
||||||
suggestions = getUserSuggestions(mentionableUsers, searchedText, -1)
|
searchedText := strings.ToLower(subs(fullText, atSignIdx+1, end))
|
||||||
|
m.logger.Debug("calculateSuggestionsWithMentionableUsers", zap.Int("atSignIdx", atSignIdx), zap.String("searchedText", searchedText), zap.String("fullText", fullText), zap.Any("state", state), zap.Int("end", end))
|
||||||
|
if end-atSignIdx <= 100 {
|
||||||
|
suggestions = getUserSuggestions(mentionableUsers, searchedText, -1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.AtSignIdx = atSignIdx
|
state.AtSignIdx = atSignIdx
|
||||||
state.AtIdxs = newAtIdxs
|
state.AtIdxs = newAtIndexEntries
|
||||||
state.MentionEnd = end
|
state.MentionEnd = end
|
||||||
ctx.InputSegments = calculatedInput
|
ctx.InputSegments = calculatedInput
|
||||||
ctx.MentionSuggestions = suggestions
|
ctx.MentionSuggestions = suggestions
|
||||||
|
@ -403,7 +408,13 @@ func (m *MentionManager) SelectMention(chatID, text, primaryName, publicKey stri
|
||||||
|
|
||||||
ctx.NewText = string(tr[:atSignIdx+1]) + primaryName + space + string(tr[mentionEnd:])
|
ctx.NewText = string(tr[:atSignIdx+1]) + primaryName + space + string(tr[mentionEnd:])
|
||||||
|
|
||||||
return m.recheckAtIdxs(chatID, ctx.NewText, publicKey)
|
ctx, err := m.recheckAtIdxs(chatID, ctx.NewText, publicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ctx.PreviousText = ctx.NewText
|
||||||
|
m.clearSuggestions(chatID)
|
||||||
|
return ctx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MentionManager) clearSuggestions(chatID string) {
|
func (m *MentionManager) clearSuggestions(chatID string) {
|
||||||
|
@ -420,7 +431,7 @@ func (m *MentionManager) ClearMentions(chatID string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MentionManager) ToInputField(chatID, text string) (*ChatMentionContext, error) {
|
func (m *MentionManager) ToInputField(chatID, text string) (*ChatMentionContext, error) {
|
||||||
mentionableUsers, err := m.getMentionableUsers(chatID)
|
mentionableUsers, err := m.mentionableUserGetter.getMentionableUsers(chatID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -821,25 +832,20 @@ type AtIndexEntry struct {
|
||||||
Checked bool
|
Checked bool
|
||||||
|
|
||||||
Mentioned bool
|
Mentioned bool
|
||||||
Mention bool
|
|
||||||
NextAtIdx int
|
NextAtIdx int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *AtIndexEntry) String() string {
|
||||||
|
return fmt.Sprintf("{From: %d, To: %d, Checked: %t, Mentioned: %t, NextAtIdx: %d}", e.From, e.To, e.Checked, e.Mentioned, e.NextAtIdx)
|
||||||
|
}
|
||||||
|
|
||||||
// implementation reference: https://github.com/status-im/status-react/blob/04d0252e013d9c67862e77a3467dd32c3abde934/src/status_im/chat/models/mentions.cljs#L433
|
// implementation reference: https://github.com/status-im/status-react/blob/04d0252e013d9c67862e77a3467dd32c3abde934/src/status_im/chat/models/mentions.cljs#L433
|
||||||
func calcAtIdxs(state *MentionState) []*AtIndexEntry {
|
func calcAtIdxs(state *MentionState) []*AtIndexEntry {
|
||||||
newIdxs := getAtSignIdxs(*state.NewText, state.Start)
|
newAtSignIndexes := getAtSignIdxs(state.NewText, state.Start)
|
||||||
newIdxCnt := len(newIdxs)
|
newAtSignIndexesCount := len(newAtSignIndexes)
|
||||||
var lastNewIdx *int
|
|
||||||
if newIdxCnt > 0 {
|
|
||||||
idx := newIdxs[newIdxCnt-1]
|
|
||||||
lastNewIdx = &idx
|
|
||||||
}
|
|
||||||
newTextLen := len([]rune(*state.NewText))
|
|
||||||
oldTextLen := len([]rune(state.PreviousText))
|
|
||||||
oldEnd := state.Start + oldTextLen
|
|
||||||
if len(state.AtIdxs) == 0 {
|
if len(state.AtIdxs) == 0 {
|
||||||
result := make([]*AtIndexEntry, newIdxCnt)
|
result := make([]*AtIndexEntry, newAtSignIndexesCount)
|
||||||
for i, idx := range newIdxs {
|
for i, idx := range newAtSignIndexes {
|
||||||
result[i] = &AtIndexEntry{
|
result[i] = &AtIndexEntry{
|
||||||
From: idx,
|
From: idx,
|
||||||
Checked: false,
|
Checked: false,
|
||||||
|
@ -848,6 +854,9 @@ func calcAtIdxs(state *MentionState) []*AtIndexEntry {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newTextLen := len([]rune(state.NewText))
|
||||||
|
oldTextLen := len([]rune(state.PreviousText))
|
||||||
|
oldEnd := state.Start + oldTextLen
|
||||||
diff := newTextLen - oldTextLen
|
diff := newTextLen - oldTextLen
|
||||||
var keptAtIdxs []*AtIndexEntry
|
var keptAtIdxs []*AtIndexEntry
|
||||||
for _, entry := range state.AtIdxs {
|
for _, entry := range state.AtIdxs {
|
||||||
|
@ -870,21 +879,26 @@ func calcAtIdxs(state *MentionState) []*AtIndexEntry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var newState []*AtIndexEntry
|
var newAtIndexEntries []*AtIndexEntry
|
||||||
var added bool
|
var added bool
|
||||||
|
var lastNewIdx *int
|
||||||
|
if newAtSignIndexesCount > 0 {
|
||||||
|
idx := newAtSignIndexes[newAtSignIndexesCount-1]
|
||||||
|
lastNewIdx = &idx
|
||||||
|
}
|
||||||
for _, entry := range keptAtIdxs {
|
for _, entry := range keptAtIdxs {
|
||||||
if lastNewIdx != nil && entry.From > *lastNewIdx && !added {
|
if lastNewIdx != nil && entry.From > *lastNewIdx && !added {
|
||||||
newState = append(newState, makeAtIdxs(newIdxs)...)
|
newAtIndexEntries = append(newAtIndexEntries, makeAtIdxs(newAtSignIndexes)...)
|
||||||
newState = append(newState, entry)
|
newAtIndexEntries = append(newAtIndexEntries, entry)
|
||||||
added = true
|
added = true
|
||||||
} else {
|
} else {
|
||||||
newState = append(newState, entry)
|
newAtIndexEntries = append(newAtIndexEntries, entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !added {
|
if !added {
|
||||||
newState = append(newState, makeAtIdxs(newIdxs)...)
|
newAtIndexEntries = append(newAtIndexEntries, makeAtIdxs(newAtSignIndexes)...)
|
||||||
}
|
}
|
||||||
return newState
|
return newAtIndexEntries
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeAtIdxs(idxs []int) []*AtIndexEntry {
|
func makeAtIdxs(idxs []int) []*AtIndexEntry {
|
||||||
|
@ -898,26 +912,28 @@ func makeAtIdxs(idxs []int) []*AtIndexEntry {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAtSignIdxs(text string, start int) []int {
|
// getAtSignIdxs returns the indexes of all @ signs in the text.
|
||||||
return getAtSignIdxsHelper(text, start, 0, []int{})
|
// delta is the offset of the text within the original text.
|
||||||
|
func getAtSignIdxs(text string, delta int) []int {
|
||||||
|
return getAtSignIdxsHelper(text, delta, 0, []int{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAtSignIdxsHelper(text string, start int, from int, idxs []int) []int {
|
func getAtSignIdxsHelper(text string, delta int, from int, idxs []int) []int {
|
||||||
tr := []rune(text)
|
tr := []rune(text)
|
||||||
idx := strings.Index(string(tr[from:]), charAtSign)
|
idx := strings.Index(string(tr[from:]), charAtSign)
|
||||||
if idx != -1 {
|
if idx != -1 {
|
||||||
idx += from
|
idx += from
|
||||||
idxs = append(idxs, start+idx)
|
idxs = append(idxs, delta+idx)
|
||||||
return getAtSignIdxsHelper(text, start, idx+1, idxs)
|
return getAtSignIdxsHelper(text, delta, idx+1, idxs)
|
||||||
}
|
}
|
||||||
return idxs
|
return idxs
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkEntry(text string, entry *AtIndexEntry, mentionableUsers map[string]*MentionableUser) *AtIndexEntry {
|
func checkAtIndexEntry(fullText string, entry *AtIndexEntry, mentionableUsers map[string]*MentionableUser) *AtIndexEntry {
|
||||||
if entry.Checked {
|
if entry.Checked {
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
result := MatchMention(text+charAtSign, mentionableUsers, entry.From)
|
result := MatchMention(fullText+charAtSign, mentionableUsers, entry.From)
|
||||||
if result != nil && result.Match != "" {
|
if result != nil && result.Match != "" {
|
||||||
return &AtIndexEntry{
|
return &AtIndexEntry{
|
||||||
From: entry.From,
|
From: entry.From,
|
||||||
|
@ -928,36 +944,34 @@ func checkEntry(text string, entry *AtIndexEntry, mentionableUsers map[string]*M
|
||||||
}
|
}
|
||||||
return &AtIndexEntry{
|
return &AtIndexEntry{
|
||||||
From: entry.From,
|
From: entry.From,
|
||||||
To: len([]rune(text)),
|
To: len([]rune(fullText)),
|
||||||
Checked: true,
|
Checked: true,
|
||||||
Mention: false, // Mention vs Mentioned? wrong spelling?
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkIdxForMentions(text string, idxs []*AtIndexEntry, mentionableUsers map[string]*MentionableUser) []*AtIndexEntry {
|
func checkIdxForMentions(fullText string, currentAtIndexEntries []*AtIndexEntry, mentionableUsers map[string]*MentionableUser) []*AtIndexEntry {
|
||||||
var newIdxs []*AtIndexEntry
|
var newIndexEntries []*AtIndexEntry
|
||||||
for _, entry := range idxs {
|
for _, entry := range currentAtIndexEntries {
|
||||||
previousEntryIdx := len(newIdxs) - 1
|
previousEntryIdx := len(newIndexEntries) - 1
|
||||||
newEntry := checkEntry(text, entry, mentionableUsers)
|
newEntry := checkAtIndexEntry(fullText, entry, mentionableUsers)
|
||||||
if previousEntryIdx >= 0 && !newIdxs[previousEntryIdx].Mentioned {
|
if previousEntryIdx >= 0 && !newIndexEntries[previousEntryIdx].Mentioned {
|
||||||
newIdxs[previousEntryIdx].To = entry.From - 1
|
newIndexEntries[previousEntryIdx].To = entry.From - 1
|
||||||
}
|
}
|
||||||
if previousEntryIdx >= 0 {
|
if previousEntryIdx >= 0 {
|
||||||
newIdxs[previousEntryIdx].NextAtIdx = entry.From
|
newIndexEntries[previousEntryIdx].NextAtIdx = entry.From
|
||||||
}
|
}
|
||||||
// simulate (dissoc new-entry :next-at-idx)
|
|
||||||
newEntry.NextAtIdx = intUnknown
|
newEntry.NextAtIdx = intUnknown
|
||||||
newIdxs = append(newIdxs, newEntry)
|
newIndexEntries = append(newIndexEntries, newEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(newIdxs) > 0 {
|
if len(newIndexEntries) > 0 {
|
||||||
lastIdx := len(newIdxs) - 1
|
lastIdx := len(newIndexEntries) - 1
|
||||||
if newIdxs[lastIdx].Mentioned {
|
if newIndexEntries[lastIdx].Mentioned {
|
||||||
return newIdxs
|
return newIndexEntries
|
||||||
}
|
}
|
||||||
newIdxs[lastIdx].To = len([]rune(text)) - 1
|
newIndexEntries[lastIdx].To = len([]rune(fullText)) - 1
|
||||||
newIdxs[lastIdx].Checked = false
|
newIndexEntries[lastIdx].Checked = false
|
||||||
return newIdxs
|
return newIndexEntries
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1129,7 +1143,7 @@ func toInfo(inputSegments []InputSegment) *MentionState {
|
||||||
AtIdxs: []*AtIndexEntry{},
|
AtIdxs: []*AtIndexEntry{},
|
||||||
MentionEnd: 0,
|
MentionEnd: 0,
|
||||||
PreviousText: "",
|
PreviousText: "",
|
||||||
NewText: &newText,
|
NewText: newText,
|
||||||
Start: intUnknown,
|
Start: intUnknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1157,8 +1171,7 @@ func toInfo(inputSegments []InputSegment) *MentionState {
|
||||||
}
|
}
|
||||||
|
|
||||||
state.MentionEnd += len(tr)
|
state.MentionEnd += len(tr)
|
||||||
nt := string(tr[len(tr)-1])
|
state.NewText = string(tr[len(tr)-1])
|
||||||
state.NewText = &nt
|
|
||||||
state.Start += len(tr)
|
state.Start += len(tr)
|
||||||
state.End += len(tr)
|
state.End += len(tr)
|
||||||
}
|
}
|
||||||
|
@ -1201,26 +1214,20 @@ func reverse(r []rune) string {
|
||||||
return string(r)
|
return string(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type textOperation int
|
||||||
|
|
||||||
|
const (
|
||||||
|
textOperationAdd textOperation = iota + 1
|
||||||
|
textOperationDelete
|
||||||
|
textOperationReplace
|
||||||
|
)
|
||||||
|
|
||||||
type TextDiff struct {
|
type TextDiff struct {
|
||||||
previousText string
|
previousText string
|
||||||
newText string // we always set it to empty if it's a delete operation
|
newText string // if add operation, newText is the added text; if replace operation, newText is the text used to replace the previousText
|
||||||
start int
|
start int // start index of the operation relate to previousText
|
||||||
end int
|
end int // end index of the operation relate to previousText, always the same as start if the operation is add, range: start<=end<=len(previousText)-1
|
||||||
}
|
operation textOperation
|
||||||
|
|
||||||
// hasCommonCharSequence checks if str1 has a common character sequence with str2.
|
|
||||||
// It iterates through both strings and compares their characters one by one.
|
|
||||||
// The function returns true if all characters in str1 can be found in str2 in the same order, but not necessarily consecutively.
|
|
||||||
// This is helpful for determining if there is an insertion or deletion operation between two strings.
|
|
||||||
func hasCommonCharSequence(str1, str2 []rune) bool {
|
|
||||||
i, j := 0, 0
|
|
||||||
for i < len(str1) && j < len(str2) {
|
|
||||||
if str1[i] == str2[j] {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
j++
|
|
||||||
}
|
|
||||||
return i == len(str1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func diffText(oldText, newText string) *TextDiff {
|
func diffText(oldText, newText string) *TextDiff {
|
||||||
|
@ -1232,10 +1239,10 @@ func diffText(oldText, newText string) *TextDiff {
|
||||||
oldLen := len(t1)
|
oldLen := len(t1)
|
||||||
newLen := len(t2)
|
newLen := len(t2)
|
||||||
if oldLen == 0 {
|
if oldLen == 0 {
|
||||||
return &TextDiff{previousText: oldText, newText: newText, start: 0, end: 0}
|
return &TextDiff{previousText: oldText, newText: newText, start: 0, end: 0, operation: textOperationAdd}
|
||||||
}
|
}
|
||||||
if newLen == 0 {
|
if newLen == 0 {
|
||||||
return &TextDiff{previousText: oldText, newText: "", start: 0, end: oldLen}
|
return &TextDiff{previousText: oldText, newText: "", start: 0, end: oldLen, operation: textOperationReplace}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we reach here, t1 and t2 are not empty
|
// if we reach here, t1 and t2 are not empty
|
||||||
|
@ -1251,16 +1258,21 @@ func diffText(oldText, newText string) *TextDiff {
|
||||||
}
|
}
|
||||||
|
|
||||||
diff := &TextDiff{previousText: oldText, start: start}
|
diff := &TextDiff{previousText: oldText, start: start}
|
||||||
if hasCommonCharSequence(t1, t2) { // is just a insert operation
|
if newLen > oldLen && (start == oldLen || oldEnd == 0 || start == oldEnd) {
|
||||||
|
diff.operation = textOperationAdd
|
||||||
diff.end = start
|
diff.end = start
|
||||||
diff.newText = string(t2[start:newEnd])
|
diff.newText = string(t2[start:newEnd])
|
||||||
|
} else if newLen < oldLen && (start == newLen || newEnd == 0 || start == newEnd) {
|
||||||
|
diff.operation = textOperationDelete
|
||||||
|
diff.end = oldEnd - 1
|
||||||
} else {
|
} else {
|
||||||
diff.end = newEnd
|
diff.operation = textOperationReplace
|
||||||
if oldLen > newLen {
|
if start == 0 && oldEnd == oldLen { // full replace
|
||||||
diff.end = oldEnd
|
diff.end = oldLen - 1
|
||||||
}
|
diff.newText = newText
|
||||||
if !hasCommonCharSequence(t2, t1) { // is not a delete operation
|
} else { // partial replace
|
||||||
diff.newText = string(t2[start:diff.end])
|
diff.end = oldEnd - 1
|
||||||
|
diff.newText = string(t2[start:newEnd])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return diff
|
return diff
|
||||||
|
|
|
@ -252,12 +252,11 @@ func TestGetAtSignIdxs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCalcAtIdxs(t *testing.T) {
|
func TestCalcAtIdxs(t *testing.T) {
|
||||||
newText := "@abc"
|
|
||||||
state := MentionState{
|
state := MentionState{
|
||||||
AtIdxs: []*AtIndexEntry{
|
AtIdxs: []*AtIndexEntry{
|
||||||
{From: 0, To: 3, Checked: false},
|
{From: 0, To: 3, Checked: false},
|
||||||
},
|
},
|
||||||
NewText: &newText,
|
NewText: "@abc",
|
||||||
PreviousText: "",
|
PreviousText: "",
|
||||||
Start: 0,
|
Start: 0,
|
||||||
}
|
}
|
||||||
|
@ -288,7 +287,7 @@ func TestToInfo(t *testing.T) {
|
||||||
},
|
},
|
||||||
MentionEnd: 19,
|
MentionEnd: 19,
|
||||||
PreviousText: "",
|
PreviousText: "",
|
||||||
NewText: &newText,
|
NewText: newText,
|
||||||
Start: 18,
|
Start: 18,
|
||||||
End: 18,
|
End: 18,
|
||||||
}
|
}
|
||||||
|
@ -505,6 +504,9 @@ func TestLastIndexOf(t *testing.T) {
|
||||||
//at-sign-idx 0 text @t searched-text t start 2 end 2 new-text
|
//at-sign-idx 0 text @t searched-text t start 2 end 2 new-text
|
||||||
atSignIdx = lastIndexOf("@t", charAtSign, 2)
|
atSignIdx = lastIndexOf("@t", charAtSign, 2)
|
||||||
require.Equal(t, 0, atSignIdx)
|
require.Equal(t, 0, atSignIdx)
|
||||||
|
|
||||||
|
atSignIdx = lastIndexOf("at", charAtSign, 3)
|
||||||
|
require.Equal(t, -1, atSignIdx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDiffText(t *testing.T) {
|
func TestDiffText(t *testing.T) {
|
||||||
|
@ -521,6 +523,7 @@ func TestDiffText(t *testing.T) {
|
||||||
end: 0,
|
end: 0,
|
||||||
previousText: "",
|
previousText: "",
|
||||||
newText: "A",
|
newText: "A",
|
||||||
|
operation: textOperationAdd,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -531,6 +534,7 @@ func TestDiffText(t *testing.T) {
|
||||||
end: 1,
|
end: 1,
|
||||||
previousText: "A",
|
previousText: "A",
|
||||||
newText: "b",
|
newText: "b",
|
||||||
|
operation: textOperationAdd,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -541,16 +545,18 @@ func TestDiffText(t *testing.T) {
|
||||||
end: 2,
|
end: 2,
|
||||||
previousText: "Ab",
|
previousText: "Ab",
|
||||||
newText: "c",
|
newText: "c",
|
||||||
|
operation: textOperationAdd,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
oldText: "Abc",
|
oldText: "Ab",
|
||||||
newText: "Ac",
|
newText: "cAb",
|
||||||
expected: &TextDiff{
|
expected: &TextDiff{
|
||||||
start: 1,
|
start: 0,
|
||||||
end: 2,
|
end: 0,
|
||||||
previousText: "Abc",
|
previousText: "Ab",
|
||||||
newText: "",
|
newText: "c",
|
||||||
|
operation: textOperationAdd,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -561,6 +567,7 @@ func TestDiffText(t *testing.T) {
|
||||||
end: 1,
|
end: 1,
|
||||||
previousText: "Ac",
|
previousText: "Ac",
|
||||||
newText: "d",
|
newText: "d",
|
||||||
|
operation: textOperationAdd,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -571,6 +578,7 @@ func TestDiffText(t *testing.T) {
|
||||||
end: 2,
|
end: 2,
|
||||||
previousText: "Adc",
|
previousText: "Adc",
|
||||||
newText: " ee ",
|
newText: " ee ",
|
||||||
|
operation: textOperationAdd,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -581,6 +589,62 @@ func TestDiffText(t *testing.T) {
|
||||||
end: 1,
|
end: 1,
|
||||||
previousText: "Ad ee c",
|
previousText: "Ad ee c",
|
||||||
newText: " fff ",
|
newText: " fff ",
|
||||||
|
operation: textOperationAdd,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldText: "Abc",
|
||||||
|
newText: "Ac",
|
||||||
|
expected: &TextDiff{
|
||||||
|
start: 1,
|
||||||
|
end: 1,
|
||||||
|
previousText: "Abc",
|
||||||
|
newText: "",
|
||||||
|
operation: textOperationDelete,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldText: "Abcd",
|
||||||
|
newText: "Ab",
|
||||||
|
expected: &TextDiff{
|
||||||
|
start: 2,
|
||||||
|
end: 3,
|
||||||
|
previousText: "Abcd",
|
||||||
|
newText: "",
|
||||||
|
operation: textOperationDelete,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldText: "Abcd",
|
||||||
|
newText: "bcd",
|
||||||
|
expected: &TextDiff{
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
previousText: "Abcd",
|
||||||
|
newText: "",
|
||||||
|
operation: textOperationDelete,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldText: "Abcd你好",
|
||||||
|
newText: "Abcd你",
|
||||||
|
expected: &TextDiff{
|
||||||
|
start: 5,
|
||||||
|
end: 5,
|
||||||
|
previousText: "Abcd你好",
|
||||||
|
newText: "",
|
||||||
|
operation: textOperationDelete,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldText: "Abcd你好",
|
||||||
|
newText: "Abcd",
|
||||||
|
expected: &TextDiff{
|
||||||
|
start: 4,
|
||||||
|
end: 5,
|
||||||
|
previousText: "Abcd你好",
|
||||||
|
newText: "",
|
||||||
|
operation: textOperationDelete,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -588,9 +652,10 @@ func TestDiffText(t *testing.T) {
|
||||||
newText: " fff d ee c",
|
newText: " fff d ee c",
|
||||||
expected: &TextDiff{
|
expected: &TextDiff{
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 1,
|
end: 0,
|
||||||
previousText: "A fff d ee c",
|
previousText: "A fff d ee c",
|
||||||
newText: "",
|
newText: "",
|
||||||
|
operation: textOperationDelete,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -598,9 +663,10 @@ func TestDiffText(t *testing.T) {
|
||||||
newText: " fffee c",
|
newText: " fffee c",
|
||||||
expected: &TextDiff{
|
expected: &TextDiff{
|
||||||
start: 4,
|
start: 4,
|
||||||
end: 7,
|
end: 6,
|
||||||
previousText: " fff d ee c",
|
previousText: " fff d ee c",
|
||||||
newText: "",
|
newText: "",
|
||||||
|
operation: textOperationDelete,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -608,6 +674,94 @@ func TestDiffText(t *testing.T) {
|
||||||
newText: "abc",
|
newText: "abc",
|
||||||
expected: nil,
|
expected: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
oldText: "abc",
|
||||||
|
newText: "ghij",
|
||||||
|
expected: &TextDiff{
|
||||||
|
start: 0,
|
||||||
|
end: 2,
|
||||||
|
previousText: "abc",
|
||||||
|
newText: "ghij",
|
||||||
|
operation: textOperationReplace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldText: "abc",
|
||||||
|
newText: "babcd",
|
||||||
|
expected: &TextDiff{
|
||||||
|
start: 0,
|
||||||
|
end: 2,
|
||||||
|
previousText: "abc",
|
||||||
|
newText: "babcd",
|
||||||
|
operation: textOperationReplace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldText: "abc",
|
||||||
|
newText: "baebcd",
|
||||||
|
expected: &TextDiff{
|
||||||
|
start: 0,
|
||||||
|
end: 2,
|
||||||
|
previousText: "abc",
|
||||||
|
newText: "baebcd",
|
||||||
|
operation: textOperationReplace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldText: "abc",
|
||||||
|
newText: "aefc",
|
||||||
|
expected: &TextDiff{
|
||||||
|
start: 1,
|
||||||
|
end: 1,
|
||||||
|
previousText: "abc",
|
||||||
|
newText: "ef",
|
||||||
|
operation: textOperationReplace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldText: "abc",
|
||||||
|
newText: "adc",
|
||||||
|
expected: &TextDiff{
|
||||||
|
start: 1,
|
||||||
|
end: 1,
|
||||||
|
previousText: "abc",
|
||||||
|
newText: "d",
|
||||||
|
operation: textOperationReplace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldText: "abc",
|
||||||
|
newText: "abd",
|
||||||
|
expected: &TextDiff{
|
||||||
|
start: 2,
|
||||||
|
end: 2,
|
||||||
|
previousText: "abc",
|
||||||
|
newText: "d",
|
||||||
|
operation: textOperationReplace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldText: "abc",
|
||||||
|
newText: "cbc",
|
||||||
|
expected: &TextDiff{
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
previousText: "abc",
|
||||||
|
newText: "c",
|
||||||
|
operation: textOperationReplace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
oldText: "abc",
|
||||||
|
newText: "ffbc",
|
||||||
|
expected: &TextDiff{
|
||||||
|
start: 0,
|
||||||
|
end: 0,
|
||||||
|
previousText: "abc",
|
||||||
|
newText: "ff",
|
||||||
|
operation: textOperationReplace,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) {
|
||||||
|
@ -641,16 +795,19 @@ func TestMentionSuggestionCases(t *testing.T) {
|
||||||
{"@u2", 1},
|
{"@u2", 1},
|
||||||
{"@u23", 0},
|
{"@u23", 0},
|
||||||
{"@u2", 1},
|
{"@u2", 1},
|
||||||
|
{"@u2 abc", 0},
|
||||||
|
{"@u2 abc @u3", 1},
|
||||||
|
{"@u2 abc@u3", 0},
|
||||||
|
{"@u2 abc@u3 ", 0},
|
||||||
|
{"@u2 abc @u3", 1},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range testCases {
|
for i, tc := range testCases {
|
||||||
t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) {
|
||||||
_, err := mentionManager.OnChangeText(chatID, tc.inputText)
|
ctx, err := mentionManager.OnChangeText(chatID, tc.inputText)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
ctx, err := mentionManager.CalculateSuggestions(chatID, tc.inputText)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions))
|
|
||||||
t.Logf("Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments)
|
t.Logf("Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments)
|
||||||
|
require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -691,24 +848,19 @@ func TestMentionSuggestionSpecialChars(t *testing.T) {
|
||||||
mentionableUserMap, chatID, mentionManager := setupMentionSuggestionTest(t, nil)
|
mentionableUserMap, chatID, mentionManager := setupMentionSuggestionTest(t, nil)
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
inputText string
|
inputText string
|
||||||
expectedSize int
|
expectedSize int
|
||||||
calculateSuggestion bool
|
|
||||||
}{
|
}{
|
||||||
{"'", 0, false},
|
{"'", 0},
|
||||||
{"‘", 0, true},
|
{"‘", 0},
|
||||||
{"‘@", len(mentionableUserMap), true},
|
{"‘@", len(mentionableUserMap)},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
ctx, err := mentionManager.OnChangeText(chatID, tc.inputText)
|
ctx, err := mentionManager.OnChangeText(chatID, tc.inputText)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
if tc.calculateSuggestion {
|
|
||||||
ctx, err = mentionManager.CalculateSuggestions(chatID, tc.inputText)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions))
|
|
||||||
}
|
|
||||||
t.Logf("Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments)
|
t.Logf("Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments)
|
||||||
|
require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -723,13 +875,12 @@ func TestMentionSuggestionAtSignSpaceCases(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
inputText string
|
inputText string
|
||||||
expectedSize int
|
expectedSize int
|
||||||
calculateSuggestion bool
|
|
||||||
}{
|
}{
|
||||||
{"@", len(mentionableUserMap), true},
|
{"@", len(mentionableUserMap)},
|
||||||
{"@ ", 0, true},
|
{"@ ", 0},
|
||||||
{"@ @", len(mentionableUserMap), true},
|
{"@ @", len(mentionableUserMap)},
|
||||||
}
|
}
|
||||||
|
|
||||||
var ctx *ChatMentionContext
|
var ctx *ChatMentionContext
|
||||||
|
@ -738,12 +889,7 @@ func TestMentionSuggestionAtSignSpaceCases(t *testing.T) {
|
||||||
ctx, err = mentionManager.OnChangeText(chatID, tc.inputText)
|
ctx, err = mentionManager.OnChangeText(chatID, tc.inputText)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Logf("After OnChangeText, Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments)
|
t.Logf("After OnChangeText, Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments)
|
||||||
if tc.calculateSuggestion {
|
require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions))
|
||||||
ctx, err = mentionManager.CalculateSuggestions(chatID, tc.inputText)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, tc.expectedSize, len(ctx.MentionSuggestions))
|
|
||||||
t.Logf("After CalculateSuggestions, Input: %+v, MentionState:%+v, InputSegments:%+v\n", tc.inputText, ctx.MentionState, ctx.InputSegments)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
require.Len(t, ctx.InputSegments, 3)
|
require.Len(t, ctx.InputSegments, 3)
|
||||||
require.Equal(t, Mention, ctx.InputSegments[0].Type)
|
require.Equal(t, Mention, ctx.InputSegments[0].Type)
|
||||||
|
@ -754,6 +900,29 @@ func TestMentionSuggestionAtSignSpaceCases(t *testing.T) {
|
||||||
require.Equal(t, "@", ctx.InputSegments[2].Value)
|
require.Equal(t, "@", ctx.InputSegments[2].Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSelectMention(t *testing.T) {
|
||||||
|
_, chatID, mentionManager := setupMentionSuggestionTest(t, nil)
|
||||||
|
|
||||||
|
text := "@u2 abc"
|
||||||
|
ctx, err := mentionManager.OnChangeText(chatID, text)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 0, len(ctx.MentionSuggestions))
|
||||||
|
|
||||||
|
ctx, err = mentionManager.OnChangeText(chatID, "@u abc")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 3, len(ctx.MentionSuggestions))
|
||||||
|
|
||||||
|
ctx, err = mentionManager.SelectMention(chatID, "@u abc", "u2", "0xpk2")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 0, len(ctx.MentionSuggestions))
|
||||||
|
require.Equal(t, text, ctx.NewText)
|
||||||
|
require.Equal(t, text, ctx.PreviousText)
|
||||||
|
|
||||||
|
ctx, err = mentionManager.OnChangeText(chatID, text)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 0, len(ctx.MentionSuggestions))
|
||||||
|
}
|
||||||
|
|
||||||
func setupMentionSuggestionTest(t *testing.T, mentionableUserMapInput map[string]*MentionableUser) (map[string]*MentionableUser, string, *MentionManager) {
|
func setupMentionSuggestionTest(t *testing.T, mentionableUserMapInput map[string]*MentionableUser) (map[string]*MentionableUser, string, *MentionManager) {
|
||||||
mentionableUserMap := mentionableUserMapInput
|
mentionableUserMap := mentionableUserMapInput
|
||||||
if mentionableUserMap == nil {
|
if mentionableUserMap == nil {
|
||||||
|
|
Loading…
Reference in New Issue