-
-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue #22: Implement sorting using a struct to hold the state of the sorting #43
base: master
Are you sure you want to change the base?
Changes from 3 commits
cfc9cbb
ef0e73b
e0b0aef
7e78f9d
473f319
16c4dbe
fec4a76
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,11 +40,32 @@ const ( | |
helpStateFull | ||
) | ||
|
||
type sortType int | ||
|
||
const ( | ||
sortStateByName sortType = iota | ||
sortStateByPercentage | ||
//sortStateByStatements | ||
) | ||
|
||
type sortOrder bool | ||
|
||
const ( | ||
ASC sortOrder = true | ||
DSC sortOrder = false | ||
) | ||
|
||
type SortState struct { | ||
Type sortType | ||
Order sortOrder | ||
} | ||
|
||
// New create a new model that can be used directly in the tea framework. | ||
func New(opts ...Option) *Model { | ||
m := &Model{ | ||
activeView: activeViewList, | ||
helpState: helpStateShort, | ||
sortState: SortState{Type: sortStateByName, Order: ASC}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! Great way of keeping things backwards compatible 😼 |
||
codeRoot: ".", | ||
list: list.New([]list.Item{}, coverProfileDelegate{}, 0, 0), | ||
} | ||
|
@@ -78,9 +99,11 @@ type Model struct { | |
detectedPackageName string | ||
requestedFiles map[string]bool | ||
filteredLinesByFile map[string][]int | ||
profilesLoaded []*cover.Profile | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think about calling this |
||
|
||
activeView viewName | ||
helpState helpState | ||
sortState SortState | ||
ready bool | ||
|
||
err errorview.Model | ||
|
@@ -186,10 +209,8 @@ func (m *Model) onProfilesLoaded(profiles []*cover.Profile) (tea.Model, tea.Cmd) | |
return m.onError(errNoProfiles{}) | ||
} | ||
|
||
if m.sortByCoverage { | ||
sort.Slice(profiles, func(i, j int) bool { | ||
return percentCovered(profiles[i]) < percentCovered(profiles[j]) | ||
}) | ||
if m.sortByCoverage || m.sortState.Type == sortStateByPercentage { | ||
m.sortByPercentage(profiles) | ||
} | ||
|
||
m.items = make([]list.Item, len(profiles)) | ||
|
@@ -254,7 +275,18 @@ func (m *Model) onKeyPressed(key string) (tea.Model, tea.Cmd) { | |
return m, loadFile(adjustedFileName, item.profile) | ||
} | ||
|
||
return m, nil | ||
return m, nil | ||
|
||
//toggle on and insiate sortByCoverage (default = Asc) | ||
case "s": | ||
m.toggleSort() | ||
m.Update(m.profilesLoaded) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that internal bubbletea methods should not be called by our code; instead, the framework handles these calls externally, and we only need to react to them. Instead, I suggest to implement a new function The resulting code should look something like this: case "s":
m.toggleSort()
return m.updateListItems()
case "!":
m.toggleSortOrder()
return m.updateListItems() and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @orlangure After sorting the list items depending on the SortState, does calling the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I could use some help and direction on sorting by name. Is it best to sort via func (m *Model) updateListItems() tea.Cmd {
switch m.sortState.Type {
case sortStateByName:
//sort by name
case sortStateByPercentage:
//sort by percentage
}
return func() tea.Msg {
return m.loadedProfiles
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, if done properly this will cause the UI to redraw. The flow in bubbletea framework works like "action -> model changes -> rendering", and setting list items is the second step - "model changes". Rendering will follow, and will pick up the updated order of items. Regarding your second question, below is my draft of the changed functions, based on your code: func (m *Model) updateListItems() (tea.Model, tea.Cmd) {
m.sort()
m.items = make([]list.Item, len(m.profilesLoaded))
for i, p := range m.profilesLoaded {
// package name should already be set
p.FileName = strings.TrimPrefix(p.FileName, m.detectedPackageName+"/")
m.items[i] = &coverProfile{
profile: p,
percentage: percentCovered(p),
}
}
return m, m.list.SetItems(m.items)
}
func (m *Model) sort() {
switch m.sortState.Type {
case sortStateByPercentage:
m.sortByPercentage()
case sortStateByName:
m.sortByName()
}
}
func (m *Model) sortByPercentage() {
sort.Slice(m.profilesLoaded, func(i, j int) bool {
if m.sortState.Order == ASC {
return percentCovered(m.profilesLoaded[i]) < percentCovered(m.profilesLoaded[j])
} else {
return percentCovered(m.profilesLoaded[i]) > percentCovered(m.profilesLoaded[j])
}
})
}
func (m *Model) sortByName() {
sort.Slice(m.profilesLoaded, func(i, j int) bool {
if m.sortState.Order == ASC {
return m.profilesLoaded[i].FileName < m.profilesLoaded[j].FileName
} else {
return m.profilesLoaded[i].FileName > m.profilesLoaded[j].FileName
}
})
} It is not the most efficient and/or elegant way of solving the problem, but it works. It sorts the "loaded profiles" slice, and then loads the already sorted items into the list. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great stuff! I was playing around with this and nearly had this same implementation, just couldn't figure out some of the Golang syntax. Thanks for this assistance. How would you feel about pulling out this code block to have its own function. This same process is currently used twice in the code. Ref m.items = make([]list.Item, len(m.profilesLoaded))
for i, p := range m.profilesLoaded {
// package name should already be set
p.FileName = strings.TrimPrefix(p.FileName, m.detectedPackageName+"/")
m.items[i] = &coverProfile{
profile: p,
percentage: percentCovered(p),
}
} I will push my current changes and this PR should update so you can see where I am at. Thanks as always! |
||
return m, nil | ||
|
||
case "!": | ||
m.toggleSortOrder() | ||
m.Update(m.profilesLoaded) | ||
return m, nil | ||
|
||
case "?": | ||
m.toggleHelp() | ||
|
@@ -264,6 +296,38 @@ func (m *Model) onKeyPressed(key string) (tea.Model, tea.Cmd) { | |
return nil, nil | ||
} | ||
|
||
func (m *Model) toggleSortOrder() { | ||
switch m.sortState.Order { | ||
case ASC: | ||
m.sortState.Order = DSC | ||
case DSC: | ||
m.sortState.Order = ASC | ||
} | ||
} | ||
|
||
func (m *Model) sortByPercentage(profiles []*cover.Profile) { | ||
sort.Slice(profiles, func(i, j int) bool { | ||
if m.sortState.Order == ASC { | ||
return percentCovered(profiles[i]) > percentCovered(profiles[j]) | ||
} else { | ||
return percentCovered(profiles[i]) < percentCovered(profiles[j]) | ||
} | ||
}) | ||
} | ||
|
||
func (m *Model) toggleSort() { | ||
switch m.sortState.Type { | ||
case sortStateByName : | ||
m.sortState.Type = sortStateByPercentage | ||
// sort all profiles by name | ||
|
||
case sortStateByPercentage: | ||
m.sortState.Type = sortStateByName | ||
// sort all profiles by Ascend percent if isAscend is true | ||
// else sort by Descend | ||
} | ||
} | ||
|
||
func (m *Model) toggleHelp() { | ||
// manage help state globally: allow to extend or hide completely | ||
switch m.helpState { | ||
|
@@ -328,6 +392,7 @@ func (m *Model) loadProfiles(codeRoot, profileFilename string) tea.Cmd { | |
|
||
finalProfiles = append(finalProfiles, p) | ||
} | ||
m.profilesLoaded = finalProfiles | ||
|
||
return finalProfiles | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to make these exported if everything else is not exported (use lowercase letters)