Merge branch 'main' into worktree-improvise-ewi-formula-crate
# Conflicts: # TAGS
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -24,3 +24,5 @@ bench/*.txt
|
||||
proptest-regressions/
|
||||
.cursor
|
||||
.claude/worktrees/
|
||||
*~
|
||||
/roadmap.html
|
||||
|
||||
662
TAGS
662
TAGS
@ -748,7 +748,7 @@ impl<'a> Widget for ImportWizardWidget<'a> {ImportWizardWidget21,444
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {render22,489
|
||||
fn truncate(s: &str, max: usize) -> String {truncate339,13390
|
||||
|
||||
src/ui/effect.rs,14177
|
||||
src/ui/effect.rs,14356
|
||||
pub(crate) const RECORD_COORDS_CANNOT_BE_EMPTY: &str = "Record coordinates cannot be empty";RECORD_COORDS_CANNOT_BE_EMPTY9,160
|
||||
pub trait Effect: Debug {Effect13,358
|
||||
fn apply(&self, app: &mut App);apply14,384
|
||||
@ -786,223 +786,225 @@ pub struct RemoveFormula {RemoveFormula113,3206
|
||||
pub target_category: String,target_category115,3257
|
||||
impl Effect for RemoveFormula {RemoveFormula117,3292
|
||||
fn apply(&self, app: &mut App) {apply118,3324
|
||||
pub struct EnterEditAtCursor;EnterEditAtCursor128,3619
|
||||
impl Effect for EnterEditAtCursor {EnterEditAtCursor129,3649
|
||||
fn apply(&self, app: &mut App) {apply130,3685
|
||||
pub struct TogglePruneEmpty;TogglePruneEmpty148,4184
|
||||
impl Effect for TogglePruneEmpty {TogglePruneEmpty149,4213
|
||||
fn apply(&self, app: &mut App) {apply150,4248
|
||||
pub struct ToggleCatExpand(pub String);ToggleCatExpand157,4399
|
||||
impl Effect for ToggleCatExpand {ToggleCatExpand158,4439
|
||||
fn apply(&self, app: &mut App) {apply159,4473
|
||||
pub struct RemoveItem {RemoveItem167,4648
|
||||
pub category: String,category168,4672
|
||||
pub item: String,item169,4698
|
||||
impl Effect for RemoveItem {RemoveItem171,4722
|
||||
fn apply(&self, app: &mut App) {apply172,4751
|
||||
pub struct RemoveCategory(pub String);RemoveCategory178,4882
|
||||
impl Effect for RemoveCategory {RemoveCategory179,4921
|
||||
fn apply(&self, app: &mut App) {apply180,4954
|
||||
pub struct CreateView(pub String);CreateView188,5268
|
||||
impl Effect for CreateView {CreateView189,5303
|
||||
fn apply(&self, app: &mut App) {apply190,5332
|
||||
pub struct DeleteView(pub String);DeleteView196,5438
|
||||
impl Effect for DeleteView {DeleteView197,5473
|
||||
fn apply(&self, app: &mut App) {apply198,5502
|
||||
pub struct SwitchView(pub String);SwitchView204,5616
|
||||
impl Effect for SwitchView {SwitchView205,5651
|
||||
fn apply(&self, app: &mut App) {apply206,5680
|
||||
pub struct ViewBack;ViewBack221,6153
|
||||
impl Effect for ViewBack {ViewBack222,6174
|
||||
fn apply(&self, app: &mut App) {apply223,6201
|
||||
pub struct ViewForward;ViewForward238,6714
|
||||
impl Effect for ViewForward {ViewForward239,6738
|
||||
fn apply(&self, app: &mut App) {apply240,6768
|
||||
pub struct SetAxis {SetAxis254,7201
|
||||
pub category: String,category255,7222
|
||||
pub axis: Axis,axis256,7248
|
||||
impl Effect for SetAxis {SetAxis258,7270
|
||||
fn apply(&self, app: &mut App) {apply259,7296
|
||||
pub struct SetPageSelection {SetPageSelection267,7461
|
||||
pub category: String,category268,7491
|
||||
pub item: String,item269,7517
|
||||
impl Effect for SetPageSelection {SetPageSelection271,7541
|
||||
fn apply(&self, app: &mut App) {apply272,7576
|
||||
pub struct ToggleGroup {ToggleGroup280,7752
|
||||
pub category: String,category281,7777
|
||||
pub group: String,group282,7803
|
||||
impl Effect for ToggleGroup {ToggleGroup284,7828
|
||||
fn apply(&self, app: &mut App) {apply285,7858
|
||||
pub struct HideItem {HideItem293,8038
|
||||
pub category: String,category294,8060
|
||||
pub item: String,item295,8086
|
||||
impl Effect for HideItem {HideItem297,8110
|
||||
fn apply(&self, app: &mut App) {apply298,8137
|
||||
pub struct ShowItem {ShowItem306,8304
|
||||
pub category: String,category307,8326
|
||||
pub item: String,item308,8352
|
||||
impl Effect for ShowItem {ShowItem310,8376
|
||||
fn apply(&self, app: &mut App) {apply311,8403
|
||||
pub struct TransposeAxes;TransposeAxes319,8570
|
||||
impl Effect for TransposeAxes {TransposeAxes320,8596
|
||||
fn apply(&self, app: &mut App) {apply321,8628
|
||||
pub struct CycleAxis(pub String);CycleAxis327,8748
|
||||
impl Effect for CycleAxis {CycleAxis328,8782
|
||||
fn apply(&self, app: &mut App) {apply329,8810
|
||||
pub struct SetNumberFormat(pub String);SetNumberFormat335,8933
|
||||
impl Effect for SetNumberFormat {SetNumberFormat336,8973
|
||||
fn apply(&self, app: &mut App) {apply337,9007
|
||||
pub struct SetSelected(pub usize, pub usize);SetSelected345,9353
|
||||
impl Effect for SetSelected {SetSelected346,9399
|
||||
fn apply(&self, app: &mut App) {apply347,9429
|
||||
pub struct SetRowOffset(pub usize);SetRowOffset353,9560
|
||||
impl Effect for SetRowOffset {SetRowOffset354,9596
|
||||
fn apply(&self, app: &mut App) {apply355,9627
|
||||
pub struct SetColOffset(pub usize);SetColOffset361,9750
|
||||
impl Effect for SetColOffset {SetColOffset362,9786
|
||||
fn apply(&self, app: &mut App) {apply363,9817
|
||||
pub struct ChangeMode(pub AppMode);ChangeMode371,10154
|
||||
impl Effect for ChangeMode {ChangeMode372,10190
|
||||
fn apply(&self, app: &mut App) {apply373,10219
|
||||
fn changes_mode(&self) -> bool {changes_mode376,10297
|
||||
pub struct SetStatus(pub String);SetStatus382,10373
|
||||
impl Effect for SetStatus {SetStatus383,10407
|
||||
fn apply(&self, app: &mut App) {apply384,10435
|
||||
pub struct MarkDirty;MarkDirty390,10539
|
||||
impl Effect for MarkDirty {MarkDirty391,10561
|
||||
fn apply(&self, app: &mut App) {apply392,10589
|
||||
pub struct SetYanked(pub Option<CellValue>);SetYanked398,10678
|
||||
impl Effect for SetYanked {SetYanked399,10723
|
||||
fn apply(&self, app: &mut App) {apply400,10751
|
||||
pub struct SetSearchQuery(pub String);SetSearchQuery406,10851
|
||||
impl Effect for SetSearchQuery {SetSearchQuery407,10890
|
||||
fn apply(&self, app: &mut App) {apply408,10923
|
||||
pub struct SetSearchMode(pub bool);SetSearchMode414,11029
|
||||
impl Effect for SetSearchMode {SetSearchMode415,11065
|
||||
fn apply(&self, app: &mut App) {apply416,11097
|
||||
pub struct SetBuffer {SetBuffer423,11229
|
||||
pub name: String,name424,11252
|
||||
pub value: String,value425,11274
|
||||
impl Effect for SetBuffer {SetBuffer427,11299
|
||||
fn apply(&self, app: &mut App) {apply428,11327
|
||||
pub struct SetTileCatIdx(pub usize);SetTileCatIdx439,11655
|
||||
impl Effect for SetTileCatIdx {SetTileCatIdx440,11692
|
||||
fn apply(&self, app: &mut App) {apply441,11724
|
||||
pub struct StartDrill(pub Vec<(CellKey, CellValue)>);StartDrill449,11923
|
||||
impl Effect for StartDrill {StartDrill450,11977
|
||||
fn apply(&self, app: &mut App) {apply451,12006
|
||||
pub struct ApplyAndClearDrill;ApplyAndClearDrill461,12321
|
||||
impl Effect for ApplyAndClearDrill {ApplyAndClearDrill462,12352
|
||||
fn apply(&self, app: &mut App) {apply463,12389
|
||||
pub struct SetDrillPendingEdit {SetDrillPendingEdit523,14762
|
||||
pub record_idx: usize,record_idx524,14795
|
||||
pub col_name: String,col_name525,14822
|
||||
pub new_value: String,new_value526,14848
|
||||
impl Effect for SetDrillPendingEdit {SetDrillPendingEdit528,14877
|
||||
fn apply(&self, app: &mut App) {apply529,14915
|
||||
pub struct Save;Save542,15401
|
||||
impl Effect for Save {Save543,15418
|
||||
fn apply(&self, app: &mut App) {apply544,15441
|
||||
pub struct SaveAs(pub PathBuf);SaveAs562,16006
|
||||
impl Effect for SaveAs {SaveAs563,16038
|
||||
fn apply(&self, app: &mut App) {apply564,16063
|
||||
pub struct WizardKey {WizardKey582,16679
|
||||
pub key_code: crossterm::event::KeyCode,key_code583,16702
|
||||
impl Effect for WizardKey {WizardKey585,16749
|
||||
fn apply(&self, app: &mut App) {apply586,16777
|
||||
pub struct StartImportWizard(pub String);StartImportWizard712,22429
|
||||
impl Effect for StartImportWizard {StartImportWizard713,22471
|
||||
fn apply(&self, app: &mut App) {apply714,22507
|
||||
pub struct ExportCsv(pub PathBuf);ExportCsv733,23142
|
||||
impl Effect for ExportCsv {ExportCsv734,23177
|
||||
fn apply(&self, app: &mut App) {apply735,23205
|
||||
pub struct LoadModel(pub PathBuf);LoadModel750,23693
|
||||
impl Effect for LoadModel {LoadModel751,23728
|
||||
fn apply(&self, app: &mut App) {apply752,23756
|
||||
pub struct ImportJsonHeadless {ImportJsonHeadless768,24268
|
||||
pub path: PathBuf,path769,24300
|
||||
pub model_name: Option<String>,model_name770,24323
|
||||
pub array_path: Option<String>,array_path771,24359
|
||||
impl Effect for ImportJsonHeadless {ImportJsonHeadless773,24397
|
||||
fn apply(&self, app: &mut App) {apply774,24434
|
||||
pub struct SetPanelOpen {SetPanelOpen878,28005
|
||||
pub panel: Panel,panel879,28031
|
||||
pub open: bool,open880,28053
|
||||
pub enum Panel {Panel884,28106
|
||||
Formula,Formula885,28123
|
||||
Category,Category886,28136
|
||||
View,View887,28150
|
||||
impl Panel {Panel890,28163
|
||||
pub fn mode(self) -> AppMode {mode891,28176
|
||||
impl Effect for SetPanelOpen {SetPanelOpen900,28406
|
||||
fn apply(&self, app: &mut App) {apply901,28437
|
||||
pub struct SetPanelCursor {SetPanelCursor911,28731
|
||||
pub panel: Panel,panel912,28759
|
||||
pub cursor: usize,cursor913,28781
|
||||
impl Effect for SetPanelCursor {SetPanelCursor915,28806
|
||||
fn apply(&self, app: &mut App) {apply916,28839
|
||||
pub fn mark_dirty() -> Box<dyn Effect> {mark_dirty927,29301
|
||||
pub fn set_status(msg: impl Into<String>) -> Box<dyn Effect> {set_status931,29369
|
||||
pub fn change_mode(mode: AppMode) -> Box<dyn Effect> {change_mode935,29471
|
||||
pub fn set_selected(row: usize, col: usize) -> Box<dyn Effect> {set_selected939,29560
|
||||
pub struct HelpPageNext;HelpPageNext946,29870
|
||||
impl Effect for HelpPageNext {HelpPageNext947,29895
|
||||
fn apply(&self, app: &mut App) {apply948,29926
|
||||
pub struct HelpPagePrev;HelpPagePrev955,30125
|
||||
impl Effect for HelpPagePrev {HelpPagePrev956,30150
|
||||
fn apply(&self, app: &mut App) {apply957,30181
|
||||
pub struct HelpPageSet(pub usize);HelpPageSet963,30301
|
||||
impl Effect for HelpPageSet {HelpPageSet964,30336
|
||||
fn apply(&self, app: &mut App) {apply965,30366
|
||||
pub fn help_page_next() -> Box<dyn Effect> {help_page_next970,30444
|
||||
pub fn help_page_prev() -> Box<dyn Effect> {help_page_prev974,30519
|
||||
pub fn help_page_set(page: usize) -> Box<dyn Effect> {help_page_set978,30594
|
||||
mod tests {tests983,30697
|
||||
fn test_app() -> App {test_app988,30813
|
||||
fn add_category_effect() {add_category_effect1002,31444
|
||||
fn add_item_to_existing_category() {add_item_to_existing_category1009,31653
|
||||
fn add_item_to_nonexistent_category_sets_status() {add_item_to_nonexistent_category_sets_status1028,32148
|
||||
fn set_cell_and_clear_cell() {set_cell_and_clear_cell1039,32457
|
||||
fn add_formula_valid() {add_formula_valid1053,32950
|
||||
fn add_formula_adds_target_item_to_category() {add_formula_adds_target_item_to_category1067,33491
|
||||
fn add_formula_to_measure_shows_in_effective_items() {add_formula_to_measure_shows_in_effective_items1100,34562
|
||||
fn add_formula_invalid_sets_error_status() {add_formula_invalid_sets_error_status1126,35512
|
||||
fn remove_formula_effect() {remove_formula_effect1137,35829
|
||||
fn switch_view_pushes_to_back_stack() {switch_view_pushes_to_back_stack1156,36530
|
||||
fn switch_view_to_same_does_not_push_stack() {switch_view_to_same_does_not_push_stack1170,37054
|
||||
fn view_back_and_forward() {view_back_and_forward1177,37266
|
||||
fn view_back_with_empty_stack_is_noop() {view_back_with_empty_stack_is_noop1199,38100
|
||||
fn create_and_delete_view() {create_and_delete_view1207,38342
|
||||
fn set_axis_effect() {set_axis_effect1217,38667
|
||||
fn transpose_axes_effect() {transpose_axes_effect1228,38949
|
||||
fn set_selected_effect() {set_selected_effect1266,40173
|
||||
fn set_row_and_col_offset() {set_row_and_col_offset1273,40365
|
||||
fn change_mode_effect() {change_mode_effect1284,40835
|
||||
fn set_buffer_empty_clears() {set_buffer_empty_clears1294,41199
|
||||
fn set_status_effect() {set_status_effect1307,41585
|
||||
fn mark_dirty_effect() {mark_dirty_effect1314,41768
|
||||
fn set_yanked_effect() {set_yanked_effect1322,41942
|
||||
fn set_search_query_and_mode() {set_search_query_and_mode1329,42153
|
||||
fn set_buffer_normal_key() {set_buffer_normal_key1342,42664
|
||||
fn set_buffer_search_writes_to_search_query() {set_buffer_search_writes_to_search_query1353,42947
|
||||
fn set_panel_open_and_cursor() {set_panel_open_and_cursor1367,43487
|
||||
fn set_tile_cat_idx_effect() {set_tile_cat_idx_effect1399,44215
|
||||
fn help_page_navigation() {help_page_navigation1408,44561
|
||||
fn help_page_prev_clamps_at_zero() {help_page_prev_clamps_at_zero1422,44990
|
||||
fn start_drill_and_apply_clear_drill_with_no_edits() {start_drill_and_apply_clear_drill_with_no_edits1431,45343
|
||||
fn apply_and_clear_drill_with_value_edit() {apply_and_clear_drill_with_value_edit1448,45952
|
||||
fn apply_and_clear_drill_with_coord_rename() {apply_and_clear_drill_with_coord_rename1475,46849
|
||||
fn apply_and_clear_drill_empty_value_clears_cell() {apply_and_clear_drill_empty_value_clears_cell1517,48255
|
||||
fn toggle_prune_empty_effect() {toggle_prune_empty_effect1543,49214
|
||||
fn toggle_cat_expand_effect() {toggle_cat_expand_effect1553,49585
|
||||
fn remove_item_and_category() {remove_item_and_category1563,49957
|
||||
fn set_number_format_effect() {set_number_format_effect1587,50747
|
||||
fn set_page_selection_effect() {set_page_selection_effect1596,51148
|
||||
fn hide_and_show_item_effects() {hide_and_show_item_effects1609,51645
|
||||
fn toggle_group_effect() {toggle_group_effect1629,52335
|
||||
fn cycle_axis_effect() {cycle_axis_effect1656,53171
|
||||
fn save_without_file_path_shows_status() {save_without_file_path_shows_status1667,53637
|
||||
fn panel_mode_mapping() {panel_mode_mapping1676,54000
|
||||
pub struct EnterEditAtCursor {EnterEditAtCursor133,3880
|
||||
pub target_mode: AppMode,target_mode134,3911
|
||||
impl Effect for EnterEditAtCursor {EnterEditAtCursor136,3943
|
||||
fn apply(&self, app: &mut App) {apply137,3979
|
||||
pub struct TogglePruneEmpty;TogglePruneEmpty153,4532
|
||||
impl Effect for TogglePruneEmpty {TogglePruneEmpty154,4561
|
||||
fn apply(&self, app: &mut App) {apply155,4596
|
||||
pub struct ToggleCatExpand(pub String);ToggleCatExpand162,4747
|
||||
impl Effect for ToggleCatExpand {ToggleCatExpand163,4787
|
||||
fn apply(&self, app: &mut App) {apply164,4821
|
||||
pub struct RemoveItem {RemoveItem172,4996
|
||||
pub category: String,category173,5020
|
||||
pub item: String,item174,5046
|
||||
impl Effect for RemoveItem {RemoveItem176,5070
|
||||
fn apply(&self, app: &mut App) {apply177,5099
|
||||
pub struct RemoveCategory(pub String);RemoveCategory183,5230
|
||||
impl Effect for RemoveCategory {RemoveCategory184,5269
|
||||
fn apply(&self, app: &mut App) {apply185,5302
|
||||
pub struct CreateView(pub String);CreateView193,5616
|
||||
impl Effect for CreateView {CreateView194,5651
|
||||
fn apply(&self, app: &mut App) {apply195,5680
|
||||
pub struct DeleteView(pub String);DeleteView201,5786
|
||||
impl Effect for DeleteView {DeleteView202,5821
|
||||
fn apply(&self, app: &mut App) {apply203,5850
|
||||
pub struct SwitchView(pub String);SwitchView209,5964
|
||||
impl Effect for SwitchView {SwitchView210,5999
|
||||
fn apply(&self, app: &mut App) {apply211,6028
|
||||
pub struct ViewBack;ViewBack226,6501
|
||||
impl Effect for ViewBack {ViewBack227,6522
|
||||
fn apply(&self, app: &mut App) {apply228,6549
|
||||
pub struct ViewForward;ViewForward243,7062
|
||||
impl Effect for ViewForward {ViewForward244,7086
|
||||
fn apply(&self, app: &mut App) {apply245,7116
|
||||
pub struct SetAxis {SetAxis259,7549
|
||||
pub category: String,category260,7570
|
||||
pub axis: Axis,axis261,7596
|
||||
impl Effect for SetAxis {SetAxis263,7618
|
||||
fn apply(&self, app: &mut App) {apply264,7644
|
||||
pub struct SetPageSelection {SetPageSelection272,7809
|
||||
pub category: String,category273,7839
|
||||
pub item: String,item274,7865
|
||||
impl Effect for SetPageSelection {SetPageSelection276,7889
|
||||
fn apply(&self, app: &mut App) {apply277,7924
|
||||
pub struct ToggleGroup {ToggleGroup285,8100
|
||||
pub category: String,category286,8125
|
||||
pub group: String,group287,8151
|
||||
impl Effect for ToggleGroup {ToggleGroup289,8176
|
||||
fn apply(&self, app: &mut App) {apply290,8206
|
||||
pub struct HideItem {HideItem298,8386
|
||||
pub category: String,category299,8408
|
||||
pub item: String,item300,8434
|
||||
impl Effect for HideItem {HideItem302,8458
|
||||
fn apply(&self, app: &mut App) {apply303,8485
|
||||
pub struct ShowItem {ShowItem311,8652
|
||||
pub category: String,category312,8674
|
||||
pub item: String,item313,8700
|
||||
impl Effect for ShowItem {ShowItem315,8724
|
||||
fn apply(&self, app: &mut App) {apply316,8751
|
||||
pub struct TransposeAxes;TransposeAxes324,8918
|
||||
impl Effect for TransposeAxes {TransposeAxes325,8944
|
||||
fn apply(&self, app: &mut App) {apply326,8976
|
||||
pub struct CycleAxis(pub String);CycleAxis332,9096
|
||||
impl Effect for CycleAxis {CycleAxis333,9130
|
||||
fn apply(&self, app: &mut App) {apply334,9158
|
||||
pub struct SetNumberFormat(pub String);SetNumberFormat340,9281
|
||||
impl Effect for SetNumberFormat {SetNumberFormat341,9321
|
||||
fn apply(&self, app: &mut App) {apply342,9355
|
||||
pub struct SetSelected(pub usize, pub usize);SetSelected350,9701
|
||||
impl Effect for SetSelected {SetSelected351,9747
|
||||
fn apply(&self, app: &mut App) {apply352,9777
|
||||
pub struct SetRowOffset(pub usize);SetRowOffset358,9908
|
||||
impl Effect for SetRowOffset {SetRowOffset359,9944
|
||||
fn apply(&self, app: &mut App) {apply360,9975
|
||||
pub struct SetColOffset(pub usize);SetColOffset366,10098
|
||||
impl Effect for SetColOffset {SetColOffset367,10134
|
||||
fn apply(&self, app: &mut App) {apply368,10165
|
||||
pub struct ChangeMode(pub AppMode);ChangeMode376,10502
|
||||
impl Effect for ChangeMode {ChangeMode377,10538
|
||||
fn apply(&self, app: &mut App) {apply378,10567
|
||||
fn changes_mode(&self) -> bool {changes_mode381,10645
|
||||
pub struct SetStatus(pub String);SetStatus387,10721
|
||||
impl Effect for SetStatus {SetStatus388,10755
|
||||
fn apply(&self, app: &mut App) {apply389,10783
|
||||
pub struct MarkDirty;MarkDirty395,10887
|
||||
impl Effect for MarkDirty {MarkDirty396,10909
|
||||
fn apply(&self, app: &mut App) {apply397,10937
|
||||
pub struct SetYanked(pub Option<CellValue>);SetYanked403,11026
|
||||
impl Effect for SetYanked {SetYanked404,11071
|
||||
fn apply(&self, app: &mut App) {apply405,11099
|
||||
pub struct SetSearchQuery(pub String);SetSearchQuery411,11199
|
||||
impl Effect for SetSearchQuery {SetSearchQuery412,11238
|
||||
fn apply(&self, app: &mut App) {apply413,11271
|
||||
pub struct SetSearchMode(pub bool);SetSearchMode419,11377
|
||||
impl Effect for SetSearchMode {SetSearchMode420,11413
|
||||
fn apply(&self, app: &mut App) {apply421,11445
|
||||
pub struct SetBuffer {SetBuffer428,11577
|
||||
pub name: String,name429,11600
|
||||
pub value: String,value430,11622
|
||||
impl Effect for SetBuffer {SetBuffer432,11647
|
||||
fn apply(&self, app: &mut App) {apply433,11675
|
||||
pub struct SetTileCatIdx(pub usize);SetTileCatIdx444,12003
|
||||
impl Effect for SetTileCatIdx {SetTileCatIdx445,12040
|
||||
fn apply(&self, app: &mut App) {apply446,12072
|
||||
pub struct StartDrill(pub Vec<(CellKey, CellValue)>);StartDrill454,12271
|
||||
impl Effect for StartDrill {StartDrill455,12325
|
||||
fn apply(&self, app: &mut App) {apply456,12354
|
||||
pub struct ApplyAndClearDrill;ApplyAndClearDrill466,12669
|
||||
impl Effect for ApplyAndClearDrill {ApplyAndClearDrill467,12700
|
||||
fn apply(&self, app: &mut App) {apply468,12737
|
||||
pub struct SetDrillPendingEdit {SetDrillPendingEdit528,15110
|
||||
pub record_idx: usize,record_idx529,15143
|
||||
pub col_name: String,col_name530,15170
|
||||
pub new_value: String,new_value531,15196
|
||||
impl Effect for SetDrillPendingEdit {SetDrillPendingEdit533,15225
|
||||
fn apply(&self, app: &mut App) {apply534,15263
|
||||
pub struct Save;Save547,15749
|
||||
impl Effect for Save {Save548,15766
|
||||
fn apply(&self, app: &mut App) {apply549,15789
|
||||
pub struct SaveAs(pub PathBuf);SaveAs567,16354
|
||||
impl Effect for SaveAs {SaveAs568,16386
|
||||
fn apply(&self, app: &mut App) {apply569,16411
|
||||
pub struct WizardKey {WizardKey587,17027
|
||||
pub key_code: crossterm::event::KeyCode,key_code588,17050
|
||||
impl Effect for WizardKey {WizardKey590,17097
|
||||
fn apply(&self, app: &mut App) {apply591,17125
|
||||
pub struct StartImportWizard(pub String);StartImportWizard717,22777
|
||||
impl Effect for StartImportWizard {StartImportWizard718,22819
|
||||
fn apply(&self, app: &mut App) {apply719,22855
|
||||
pub struct ExportCsv(pub PathBuf);ExportCsv738,23490
|
||||
impl Effect for ExportCsv {ExportCsv739,23525
|
||||
fn apply(&self, app: &mut App) {apply740,23553
|
||||
pub struct LoadModel(pub PathBuf);LoadModel755,24041
|
||||
impl Effect for LoadModel {LoadModel756,24076
|
||||
fn apply(&self, app: &mut App) {apply757,24104
|
||||
pub struct ImportJsonHeadless {ImportJsonHeadless773,24616
|
||||
pub path: PathBuf,path774,24648
|
||||
pub model_name: Option<String>,model_name775,24671
|
||||
pub array_path: Option<String>,array_path776,24707
|
||||
impl Effect for ImportJsonHeadless {ImportJsonHeadless778,24745
|
||||
fn apply(&self, app: &mut App) {apply779,24782
|
||||
pub struct SetPanelOpen {SetPanelOpen883,28353
|
||||
pub panel: Panel,panel884,28379
|
||||
pub open: bool,open885,28401
|
||||
pub enum Panel {Panel889,28454
|
||||
Formula,Formula890,28471
|
||||
Category,Category891,28484
|
||||
View,View892,28498
|
||||
impl Panel {Panel895,28511
|
||||
pub fn mode(self) -> AppMode {mode896,28524
|
||||
impl Effect for SetPanelOpen {SetPanelOpen905,28754
|
||||
fn apply(&self, app: &mut App) {apply906,28785
|
||||
pub struct SetPanelCursor {SetPanelCursor916,29079
|
||||
pub panel: Panel,panel917,29107
|
||||
pub cursor: usize,cursor918,29129
|
||||
impl Effect for SetPanelCursor {SetPanelCursor920,29154
|
||||
fn apply(&self, app: &mut App) {apply921,29187
|
||||
pub fn mark_dirty() -> Box<dyn Effect> {mark_dirty932,29649
|
||||
pub fn set_status(msg: impl Into<String>) -> Box<dyn Effect> {set_status936,29717
|
||||
pub fn change_mode(mode: AppMode) -> Box<dyn Effect> {change_mode940,29819
|
||||
pub fn set_selected(row: usize, col: usize) -> Box<dyn Effect> {set_selected944,29908
|
||||
pub struct HelpPageNext;HelpPageNext951,30218
|
||||
impl Effect for HelpPageNext {HelpPageNext952,30243
|
||||
fn apply(&self, app: &mut App) {apply953,30274
|
||||
pub struct HelpPagePrev;HelpPagePrev960,30473
|
||||
impl Effect for HelpPagePrev {HelpPagePrev961,30498
|
||||
fn apply(&self, app: &mut App) {apply962,30529
|
||||
pub struct HelpPageSet(pub usize);HelpPageSet968,30649
|
||||
impl Effect for HelpPageSet {HelpPageSet969,30684
|
||||
fn apply(&self, app: &mut App) {apply970,30714
|
||||
pub fn help_page_next() -> Box<dyn Effect> {help_page_next975,30792
|
||||
pub fn help_page_prev() -> Box<dyn Effect> {help_page_prev979,30867
|
||||
pub fn help_page_set(page: usize) -> Box<dyn Effect> {help_page_set983,30942
|
||||
mod tests {tests988,31045
|
||||
fn test_app() -> App {test_app993,31161
|
||||
fn add_category_effect() {add_category_effect1007,31792
|
||||
fn add_item_to_existing_category() {add_item_to_existing_category1014,32001
|
||||
fn add_item_to_nonexistent_category_sets_status() {add_item_to_nonexistent_category_sets_status1033,32496
|
||||
fn set_cell_and_clear_cell() {set_cell_and_clear_cell1044,32805
|
||||
fn add_formula_valid() {add_formula_valid1058,33298
|
||||
fn add_formula_adds_target_item_to_category() {add_formula_adds_target_item_to_category1072,33839
|
||||
fn add_formula_to_measure_shows_in_effective_items() {add_formula_to_measure_shows_in_effective_items1105,34910
|
||||
fn add_formula_invalid_sets_error_status() {add_formula_invalid_sets_error_status1131,35860
|
||||
fn remove_formula_effect() {remove_formula_effect1142,36177
|
||||
fn switch_view_pushes_to_back_stack() {switch_view_pushes_to_back_stack1161,36878
|
||||
fn switch_view_to_same_does_not_push_stack() {switch_view_to_same_does_not_push_stack1175,37402
|
||||
fn view_back_and_forward() {view_back_and_forward1182,37614
|
||||
fn view_back_with_empty_stack_is_noop() {view_back_with_empty_stack_is_noop1204,38448
|
||||
fn create_and_delete_view() {create_and_delete_view1212,38690
|
||||
fn set_axis_effect() {set_axis_effect1222,39015
|
||||
fn transpose_axes_effect() {transpose_axes_effect1233,39297
|
||||
fn set_selected_effect() {set_selected_effect1271,40521
|
||||
fn set_row_and_col_offset() {set_row_and_col_offset1278,40713
|
||||
fn change_mode_effect() {change_mode_effect1289,41183
|
||||
fn enter_edit_at_cursor_uses_target_mode_not_app_mode() {enter_edit_at_cursor_uses_target_mode_not_app_mode1301,41700
|
||||
fn set_buffer_empty_clears() {set_buffer_empty_clears1334,42863
|
||||
fn set_status_effect() {set_status_effect1347,43249
|
||||
fn mark_dirty_effect() {mark_dirty_effect1354,43432
|
||||
fn set_yanked_effect() {set_yanked_effect1362,43606
|
||||
fn set_search_query_and_mode() {set_search_query_and_mode1369,43817
|
||||
fn set_buffer_normal_key() {set_buffer_normal_key1382,44328
|
||||
fn set_buffer_search_writes_to_search_query() {set_buffer_search_writes_to_search_query1393,44611
|
||||
fn set_panel_open_and_cursor() {set_panel_open_and_cursor1407,45151
|
||||
fn set_tile_cat_idx_effect() {set_tile_cat_idx_effect1439,45879
|
||||
fn help_page_navigation() {help_page_navigation1448,46225
|
||||
fn help_page_prev_clamps_at_zero() {help_page_prev_clamps_at_zero1462,46654
|
||||
fn start_drill_and_apply_clear_drill_with_no_edits() {start_drill_and_apply_clear_drill_with_no_edits1471,47007
|
||||
fn apply_and_clear_drill_with_value_edit() {apply_and_clear_drill_with_value_edit1488,47616
|
||||
fn apply_and_clear_drill_with_coord_rename() {apply_and_clear_drill_with_coord_rename1515,48513
|
||||
fn apply_and_clear_drill_empty_value_clears_cell() {apply_and_clear_drill_empty_value_clears_cell1557,49919
|
||||
fn toggle_prune_empty_effect() {toggle_prune_empty_effect1583,50878
|
||||
fn toggle_cat_expand_effect() {toggle_cat_expand_effect1593,51249
|
||||
fn remove_item_and_category() {remove_item_and_category1603,51621
|
||||
fn set_number_format_effect() {set_number_format_effect1627,52411
|
||||
fn set_page_selection_effect() {set_page_selection_effect1636,52812
|
||||
fn hide_and_show_item_effects() {hide_and_show_item_effects1649,53309
|
||||
fn toggle_group_effect() {toggle_group_effect1669,53999
|
||||
fn cycle_axis_effect() {cycle_axis_effect1696,54835
|
||||
fn save_without_file_path_shows_status() {save_without_file_path_shows_status1707,55301
|
||||
fn panel_mode_mapping() {panel_mode_mapping1716,55664
|
||||
|
||||
src/ui/mod.rs,400
|
||||
pub mod app;app1,0
|
||||
@ -1434,7 +1436,7 @@ mod tests {tests302,10085
|
||||
fn parse_axis_recognizes_all_variants() {parse_axis_recognizes_all_variants306,10128
|
||||
fn parse_axis_rejects_unknown() {parse_axis_rejects_unknown316,10462
|
||||
|
||||
src/command/cmd/commit.rs,2750
|
||||
src/command/cmd/commit.rs,2939
|
||||
mod tests {tests10,266
|
||||
fn commit_formula_with_categories_adds_formula() {commit_formula_with_categories_adds_formula18,426
|
||||
fn commit_formula_without_regular_categories_targets_measure() {commit_formula_without_regular_categories_targets_measure41,1294
|
||||
@ -1442,39 +1444,41 @@ mod tests {tests10,266
|
||||
fn commit_category_add_with_empty_buffer_returns_to_panel() {commit_category_add_with_empty_buffer_returns_to_panel79,2622
|
||||
fn commit_item_add_with_name_produces_add_item() {commit_item_add_with_name_produces_add_item96,3221
|
||||
fn commit_item_add_outside_item_add_mode_returns_empty() {commit_item_add_outside_item_add_mode_returns_empty112,3853
|
||||
fn commit_export_produces_export_and_normal_mode() {commit_export_produces_export_and_normal_mode122,4176
|
||||
fn commit_regular_cell_value(key: &CellKey, value: &str, effects: &mut Vec<Box<dyn Effect>>) {commit_regular_cell_value143,5139
|
||||
fn stage_drill_edit(record_idx: usize, col_name: String, value: &str) -> Box<dyn Effect> {stage_drill_edit158,5735
|
||||
fn commit_plain_records_edit(commit_plain_records_edit167,6034
|
||||
fn commit_cell_value(commit_cell_value208,7063
|
||||
pub enum AdvanceDir {AdvanceDir228,7649
|
||||
Down,Down230,7741
|
||||
Right,Right232,7800
|
||||
pub struct CommitAndAdvance {CommitAndAdvance238,7981
|
||||
pub key: CellKey,key239,8011
|
||||
pub value: String,value240,8033
|
||||
pub advance: AdvanceDir,advance241,8056
|
||||
pub cursor: CursorState,cursor242,8085
|
||||
impl Cmd for CommitAndAdvance {CommitAndAdvance244,8116
|
||||
fn name(&self) -> &'static str {name245,8148
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute251,8343
|
||||
pub struct CommitFormula;CommitFormula297,10180
|
||||
impl Cmd for CommitFormula {CommitFormula298,10206
|
||||
fn name(&self) -> &'static str {name299,10235
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute302,10303
|
||||
fn commit_add_from_buffer(commit_add_from_buffer322,11120
|
||||
pub struct CommitCategoryAdd;CommitCategoryAdd345,11827
|
||||
impl Cmd for CommitCategoryAdd {CommitCategoryAdd346,11857
|
||||
fn name(&self) -> &'static str {name347,11890
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute350,11963
|
||||
pub struct CommitItemAdd;CommitItemAdd362,12340
|
||||
impl Cmd for CommitItemAdd {CommitItemAdd363,12366
|
||||
fn name(&self) -> &'static str {name364,12395
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute367,12464
|
||||
pub struct CommitExport;CommitExport389,13085
|
||||
impl Cmd for CommitExport {CommitExport390,13110
|
||||
fn name(&self) -> &'static str {name391,13138
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute394,13205
|
||||
fn commit_and_advance_threads_edit_mode_to_enter_edit_at_cursor() {commit_and_advance_threads_edit_mode_to_enter_edit_at_cursor125,4399
|
||||
fn commit_export_produces_export_and_normal_mode() {commit_export_produces_export_and_normal_mode151,5424
|
||||
fn commit_regular_cell_value(key: &CellKey, value: &str, effects: &mut Vec<Box<dyn Effect>>) {commit_regular_cell_value172,6387
|
||||
fn stage_drill_edit(record_idx: usize, col_name: String, value: &str) -> Box<dyn Effect> {stage_drill_edit187,6983
|
||||
fn commit_plain_records_edit(commit_plain_records_edit196,7282
|
||||
fn commit_cell_value(commit_cell_value237,8311
|
||||
pub enum AdvanceDir {AdvanceDir257,8897
|
||||
Down,Down259,8989
|
||||
Right,Right261,9048
|
||||
pub struct CommitAndAdvance {CommitAndAdvance272,9491
|
||||
pub key: CellKey,key273,9521
|
||||
pub value: String,value274,9543
|
||||
pub advance: AdvanceDir,advance275,9566
|
||||
pub cursor: CursorState,cursor276,9595
|
||||
pub edit_mode: AppMode,edit_mode277,9624
|
||||
impl Cmd for CommitAndAdvance {CommitAndAdvance279,9654
|
||||
fn name(&self) -> &'static str {name280,9686
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute286,9881
|
||||
pub struct CommitFormula;CommitFormula334,11779
|
||||
impl Cmd for CommitFormula {CommitFormula335,11805
|
||||
fn name(&self) -> &'static str {name336,11834
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute339,11902
|
||||
fn commit_add_from_buffer(commit_add_from_buffer359,12719
|
||||
pub struct CommitCategoryAdd;CommitCategoryAdd382,13426
|
||||
impl Cmd for CommitCategoryAdd {CommitCategoryAdd383,13456
|
||||
fn name(&self) -> &'static str {name384,13489
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute387,13562
|
||||
pub struct CommitItemAdd;CommitItemAdd399,13939
|
||||
impl Cmd for CommitItemAdd {CommitItemAdd400,13965
|
||||
fn name(&self) -> &'static str {name401,13994
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute404,14063
|
||||
pub struct CommitExport;CommitExport426,14684
|
||||
impl Cmd for CommitExport {CommitExport427,14709
|
||||
fn name(&self) -> &'static str {name428,14737
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute431,14804
|
||||
|
||||
src/command/cmd/cell.rs,1886
|
||||
mod tests {tests6,90
|
||||
@ -1508,9 +1512,10 @@ impl Cmd for SaveCmd {SaveCmd191,5956
|
||||
fn name(&self) -> &'static str {name192,5979
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute195,6037
|
||||
|
||||
src/command/cmd/registry.rs,113
|
||||
pub fn default_registry() -> CmdRegistry {default_registry19,459
|
||||
macro_rules! reg_jump {reg_jump105,4345
|
||||
src/command/cmd/registry.rs,193
|
||||
fn parse_mode_name(s: &str) -> Result<AppMode, String> {parse_mode_name7,208
|
||||
pub fn default_registry() -> CmdRegistry {default_registry40,1375
|
||||
macro_rules! reg_jump {reg_jump126,5261
|
||||
|
||||
src/command/cmd/effect_cmds.rs,518
|
||||
mod tests {tests8,162
|
||||
@ -1791,59 +1796,58 @@ impl Cmd for ExitSearchMode {ExitSearchMode195,6137
|
||||
fn name(&self) -> &'static str {name196,6167
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute199,6237
|
||||
|
||||
src/command/cmd/mode.rs,3175
|
||||
src/command/cmd/mode.rs,3285
|
||||
mod tests {tests8,151
|
||||
fn enter_edit_mode_produces_editing_mode() {enter_edit_mode_produces_editing_mode14,275
|
||||
fn enter_tile_select_with_categories() {enter_tile_select_with_categories29,788
|
||||
fn enter_tile_select_no_categories() {enter_tile_select_no_categories45,1285
|
||||
fn enter_export_prompt_sets_mode() {enter_export_prompt_sets_mode56,1621
|
||||
fn force_quit_always_produces_quit_mode() {force_quit_always_produces_quit_mode70,2055
|
||||
fn save_and_quit_produces_save_then_quit() {save_and_quit_produces_save_then_quit83,2506
|
||||
fn edit_or_drill_without_aggregation_enters_edit() {edit_or_drill_without_aggregation_enters_edit96,2993
|
||||
fn enter_search_mode_sets_flag_and_clears_query() {enter_search_mode_sets_flag_and_clears_query107,3393
|
||||
pub struct EnterMode(pub AppMode);EnterMode129,4200
|
||||
impl Cmd for EnterMode {EnterMode130,4235
|
||||
fn name(&self) -> &'static str {name131,4260
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute134,4324
|
||||
pub struct ForceQuit;ForceQuit149,4842
|
||||
impl Cmd for ForceQuit {ForceQuit150,4864
|
||||
fn name(&self) -> &'static str {name151,4889
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute154,4953
|
||||
pub struct Quit;Quit161,5159
|
||||
impl Cmd for Quit {Quit162,5176
|
||||
fn name(&self) -> &'static str {name163,5196
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute166,5251
|
||||
pub struct SaveAndQuit;SaveAndQuit179,5597
|
||||
impl Cmd for SaveAndQuit {SaveAndQuit180,5621
|
||||
fn name(&self) -> &'static str {name181,5648
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute184,5704
|
||||
pub struct EnterEditMode {EnterEditMode193,6114
|
||||
pub initial_value: String,initial_value194,6141
|
||||
impl Cmd for EnterEditMode {EnterEditMode196,6174
|
||||
fn name(&self) -> &'static str {name197,6203
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute200,6272
|
||||
pub struct EditOrDrill;EditOrDrill220,6953
|
||||
impl Cmd for EditOrDrill {EditOrDrill221,6977
|
||||
fn name(&self) -> &'static str {name222,7004
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute225,7071
|
||||
pub struct EnterEditAtCursorCmd;EnterEditAtCursorCmd254,8262
|
||||
impl Cmd for EnterEditAtCursorCmd {EnterEditAtCursorCmd255,8295
|
||||
fn name(&self) -> &'static str {name256,8331
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute259,8405
|
||||
pub struct EnterExportPrompt;EnterExportPrompt266,8578
|
||||
impl Cmd for EnterExportPrompt {EnterExportPrompt267,8608
|
||||
fn name(&self) -> &'static str {name268,8641
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute271,8714
|
||||
pub struct EnterSearchMode;EnterSearchMode278,8890
|
||||
impl Cmd for EnterSearchMode {EnterSearchMode279,8918
|
||||
fn name(&self) -> &'static str {name280,8949
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute283,9009
|
||||
pub struct EnterTileSelect;EnterTileSelect293,9266
|
||||
impl Cmd for EnterTileSelect {EnterTileSelect294,9294
|
||||
fn name(&self) -> &'static str {name295,9325
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute298,9396
|
||||
fn enter_tile_select_with_categories() {enter_tile_select_with_categories14,275
|
||||
fn enter_tile_select_no_categories() {enter_tile_select_no_categories30,772
|
||||
fn enter_export_prompt_sets_mode() {enter_export_prompt_sets_mode41,1108
|
||||
fn force_quit_always_produces_quit_mode() {force_quit_always_produces_quit_mode55,1542
|
||||
fn save_and_quit_produces_save_then_quit() {save_and_quit_produces_save_then_quit68,1993
|
||||
fn edit_or_drill_without_aggregation_enters_edit() {edit_or_drill_without_aggregation_enters_edit81,2480
|
||||
fn edit_or_drill_passes_records_editing_mode_through() {edit_or_drill_passes_records_editing_mode_through101,3324
|
||||
fn enter_edit_at_cursor_cmd_passes_target_mode_to_effect() {enter_edit_at_cursor_cmd_passes_target_mode_to_effect123,4197
|
||||
fn edit_or_drill_pre_fills_edit_buffer_with_display_value() {edit_or_drill_pre_fills_edit_buffer_with_display_value143,4926
|
||||
fn enter_search_mode_sets_flag_and_clears_query() {enter_search_mode_sets_flag_and_clears_query161,5554
|
||||
pub struct EnterMode(pub AppMode);EnterMode183,6361
|
||||
impl Cmd for EnterMode {EnterMode184,6396
|
||||
fn name(&self) -> &'static str {name185,6421
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute188,6485
|
||||
pub struct ForceQuit;ForceQuit203,7003
|
||||
impl Cmd for ForceQuit {ForceQuit204,7025
|
||||
fn name(&self) -> &'static str {name205,7050
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute208,7114
|
||||
pub struct Quit;Quit215,7320
|
||||
impl Cmd for Quit {Quit216,7337
|
||||
fn name(&self) -> &'static str {name217,7357
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute220,7412
|
||||
pub struct SaveAndQuit;SaveAndQuit233,7758
|
||||
impl Cmd for SaveAndQuit {SaveAndQuit234,7782
|
||||
fn name(&self) -> &'static str {name235,7809
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute238,7865
|
||||
pub struct EditOrDrill {EditOrDrill254,8707
|
||||
pub edit_mode: AppMode,edit_mode255,8732
|
||||
impl Cmd for EditOrDrill {EditOrDrill257,8762
|
||||
fn name(&self) -> &'static str {name258,8789
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute261,8856
|
||||
pub struct EnterEditAtCursorCmd {EnterEditAtCursorCmd295,10299
|
||||
pub target_mode: AppMode,target_mode296,10333
|
||||
impl Cmd for EnterEditAtCursorCmd {EnterEditAtCursorCmd298,10365
|
||||
fn name(&self) -> &'static str {name299,10401
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute302,10475
|
||||
pub struct EnterExportPrompt;EnterExportPrompt311,10711
|
||||
impl Cmd for EnterExportPrompt {EnterExportPrompt312,10741
|
||||
fn name(&self) -> &'static str {name313,10774
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute316,10847
|
||||
pub struct EnterSearchMode;EnterSearchMode323,11023
|
||||
impl Cmd for EnterSearchMode {EnterSearchMode324,11051
|
||||
fn name(&self) -> &'static str {name325,11082
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute328,11142
|
||||
pub struct EnterTileSelect;EnterTileSelect338,11399
|
||||
impl Cmd for EnterTileSelect {EnterTileSelect339,11427
|
||||
fn name(&self) -> &'static str {name340,11458
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {execute343,11529
|
||||
|
||||
src/command/keymap.rs,5088
|
||||
src/command/keymap.rs,5093
|
||||
fn format_key_label(code: &KeyCode) -> String {format_key_label13,301
|
||||
pub enum KeyPattern {KeyPattern38,1303
|
||||
Key(KeyCode, KeyModifiers),Key40,1359
|
||||
@ -1904,29 +1908,29 @@ impl KeymapSet {KeymapSet289,9804
|
||||
pub fn dispatch(dispatch306,10224
|
||||
pub fn dispatch_transient(dispatch_transient318,10642
|
||||
pub fn default_keymaps() -> Self {default_keymaps329,10948
|
||||
mod tests {tests930,33396
|
||||
fn lookup_exact_match() {lookup_exact_match936,33587
|
||||
fn lookup_exact_with_ctrl() {lookup_exact_with_ctrl944,33877
|
||||
fn lookup_char_falls_back_to_none_mods() {lookup_char_falls_back_to_none_mods952,34175
|
||||
fn lookup_non_char_does_not_fall_back_to_none_mods() {lookup_non_char_does_not_fall_back_to_none_mods962,34633
|
||||
fn lookup_char_falls_to_any_char() {lookup_char_falls_to_any_char972,35046
|
||||
fn lookup_non_char_skips_any_char() {lookup_non_char_skips_any_char986,35414
|
||||
fn lookup_any_matches_everything() {lookup_any_matches_everything995,35722
|
||||
fn lookup_exact_takes_priority_over_any_char() {lookup_exact_takes_priority_over_any_char1009,36067
|
||||
fn lookup_any_char_takes_priority_over_any() {lookup_any_char_takes_priority_over_any1024,36509
|
||||
fn lookup_non_char_falls_to_any_not_any_char() {lookup_non_char_falls_to_any_not_any_char1039,36921
|
||||
fn lookup_ctrl_char_with_only_none_binding_falls_through() {lookup_ctrl_char_with_only_none_binding_falls_through1054,37330
|
||||
fn mode_key_normal_no_search() {mode_key_normal_no_search1065,37836
|
||||
fn mode_key_normal_with_search_overrides() {mode_key_normal_with_search_overrides1071,38005
|
||||
fn mode_key_help() {mode_key_help1077,38189
|
||||
fn mode_key_quit_returns_none() {mode_key_quit_returns_none1083,38342
|
||||
fn lookup_returns_prefix_binding() {lookup_returns_prefix_binding1091,38669
|
||||
fn lookup_returns_sequence_binding() {lookup_returns_sequence_binding1100,39000
|
||||
fn default_keymaps_has_all_modes() {default_keymaps_has_all_modes1114,39551
|
||||
fn normal_mode_has_basic_movement() {normal_mode_has_basic_movement1144,40436
|
||||
fn editing_mode_has_any_char_and_esc() {editing_mode_has_any_char_and_esc1168,41220
|
||||
fn search_mode_has_any_char_and_esc() {search_mode_has_any_char_and_esc1182,41696
|
||||
fn import_wizard_has_any_catchall() {import_wizard_has_any_catchall1194,42090
|
||||
mod tests {tests978,34976
|
||||
fn lookup_exact_match() {lookup_exact_match984,35167
|
||||
fn lookup_exact_with_ctrl() {lookup_exact_with_ctrl992,35457
|
||||
fn lookup_char_falls_back_to_none_mods() {lookup_char_falls_back_to_none_mods1000,35755
|
||||
fn lookup_non_char_does_not_fall_back_to_none_mods() {lookup_non_char_does_not_fall_back_to_none_mods1010,36213
|
||||
fn lookup_char_falls_to_any_char() {lookup_char_falls_to_any_char1020,36626
|
||||
fn lookup_non_char_skips_any_char() {lookup_non_char_skips_any_char1034,36994
|
||||
fn lookup_any_matches_everything() {lookup_any_matches_everything1043,37302
|
||||
fn lookup_exact_takes_priority_over_any_char() {lookup_exact_takes_priority_over_any_char1057,37647
|
||||
fn lookup_any_char_takes_priority_over_any() {lookup_any_char_takes_priority_over_any1072,38089
|
||||
fn lookup_non_char_falls_to_any_not_any_char() {lookup_non_char_falls_to_any_not_any_char1087,38501
|
||||
fn lookup_ctrl_char_with_only_none_binding_falls_through() {lookup_ctrl_char_with_only_none_binding_falls_through1102,38910
|
||||
fn mode_key_normal_no_search() {mode_key_normal_no_search1113,39416
|
||||
fn mode_key_normal_with_search_overrides() {mode_key_normal_with_search_overrides1119,39585
|
||||
fn mode_key_help() {mode_key_help1125,39769
|
||||
fn mode_key_quit_returns_none() {mode_key_quit_returns_none1131,39922
|
||||
fn lookup_returns_prefix_binding() {lookup_returns_prefix_binding1139,40249
|
||||
fn lookup_returns_sequence_binding() {lookup_returns_sequence_binding1148,40580
|
||||
fn default_keymaps_has_all_modes() {default_keymaps_has_all_modes1162,41131
|
||||
fn normal_mode_has_basic_movement() {normal_mode_has_basic_movement1192,42016
|
||||
fn editing_mode_has_any_char_and_esc() {editing_mode_has_any_char_and_esc1216,42800
|
||||
fn search_mode_has_any_char_and_esc() {search_mode_has_any_char_and_esc1230,43276
|
||||
fn import_wizard_has_any_catchall() {import_wizard_has_any_catchall1242,43670
|
||||
|
||||
src/command/mod.rs,80
|
||||
pub mod cmd;cmd8,323
|
||||
|
||||
9170
roadmap.org
Normal file
9170
roadmap.org
Normal file
File diff suppressed because it is too large
Load Diff
179
scripts/gen_roadmap.py
Executable file
179
scripts/gen_roadmap.py
Executable file
@ -0,0 +1,179 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate roadmap.org from bd.
|
||||
|
||||
Inverted-tree layout: each tree is rooted at an issue that nothing else
|
||||
(among the open set) depends on — the "epic" or standalone goal. Its
|
||||
children are the things blocking it, recursively. Diamonds in the DAG
|
||||
are broken into a tree by duplicating shared deps under each parent so
|
||||
emacs `[/]` cookies count whole subtrees.
|
||||
|
||||
Usage: python3 scripts/gen_roadmap.py [output-path]
|
||||
(default output: roadmap.org at the repo root)
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def bd_json(*args):
|
||||
"""Run `bd <args>` and parse JSON from stdout."""
|
||||
cmd = ['bd', *args, '--json']
|
||||
res = subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||
return json.loads(res.stdout)
|
||||
|
||||
|
||||
def main():
|
||||
repo = Path(__file__).resolve().parent.parent
|
||||
out_path = Path(sys.argv[1]) if len(sys.argv) > 1 else repo / 'roadmap.org'
|
||||
|
||||
issues = bd_json('list')
|
||||
by_id = {i['id']: i for i in issues}
|
||||
# `bd list --all` includes closed issues — used only to resolve titles
|
||||
# of already-done dep targets so we can label them in :DONE_DEPS:.
|
||||
all_issues = bd_json('list', '--all')
|
||||
all_by_id = {i['id']: i for i in all_issues}
|
||||
|
||||
# --- Build dep graph (open-issue subgraph only) ----------------------
|
||||
# child depends_on parent; in our inverted tree, parent (the goal/epic)
|
||||
# sits ABOVE its deps (children).
|
||||
tree_children = defaultdict(set)
|
||||
tree_parents = defaultdict(set)
|
||||
for i in issues:
|
||||
iid = i['id']
|
||||
for d in (i.get('dependencies') or []):
|
||||
dep = d['depends_on_id']
|
||||
if dep in by_id and iid in by_id:
|
||||
tree_children[iid].add(dep)
|
||||
tree_parents[dep].add(iid)
|
||||
|
||||
def order_key(iid):
|
||||
i = by_id[iid]
|
||||
return (
|
||||
0 if i['status'] == 'in_progress' else 1,
|
||||
i['priority'],
|
||||
iid,
|
||||
)
|
||||
|
||||
roots = sorted(
|
||||
[iid for iid in by_id if not tree_parents.get(iid)],
|
||||
key=order_key,
|
||||
)
|
||||
|
||||
# --- Helpers ---------------------------------------------------------
|
||||
status_kw = {
|
||||
'open': 'TODO',
|
||||
'in_progress': 'DOING',
|
||||
'closed': 'DONE',
|
||||
'blocked': 'WAIT',
|
||||
'deferred': 'WAIT',
|
||||
}
|
||||
|
||||
def tag(s):
|
||||
return re.sub(r'[^A-Za-z0-9_@#%]', '_', s)
|
||||
|
||||
def headline_tags(i, kind):
|
||||
tags = [kind, f"P{i['priority']}", tag(i['issue_type'])]
|
||||
if i.get('assignee'):
|
||||
tags.append('@' + tag(i['assignee']))
|
||||
return ':' + ':'.join(tags) + ':'
|
||||
|
||||
def fmt_date(s):
|
||||
return s.replace('T', ' ').rstrip('Z') if s else ''
|
||||
|
||||
def kind_of(iid):
|
||||
return 'epic' if tree_children.get(iid) else 'standalone'
|
||||
|
||||
out = []
|
||||
out.append('#+TITLE: Improvise Roadmap')
|
||||
out.append('#+AUTHOR: Edward Langley')
|
||||
out.append('#+TODO: TODO DOING WAIT | DONE')
|
||||
out.append('#+STARTUP: overview')
|
||||
out.append('#+TAGS: epic standalone P0 P1 P2 P3 P4 task feature bug')
|
||||
out.append('#+PROPERTY: COOKIE_DATA todo recursive')
|
||||
out.append('')
|
||||
out.append(
|
||||
f'Generated from ~bd list --json~. {len(issues)} open issues '
|
||||
f'organised as {len(roots)} inverted dep-trees: each root is a goal '
|
||||
'that nothing else depends on; its children are the deps blocking '
|
||||
'it. Diamonds in the DAG are duplicated so each tree stands alone.'
|
||||
)
|
||||
out.append('')
|
||||
|
||||
def emit(iid, depth, path):
|
||||
i = by_id[iid]
|
||||
kind = kind_of(iid)
|
||||
kw = status_kw.get(i['status'], 'TODO')
|
||||
title = i['title'].replace('[', '(').replace(']', ')')
|
||||
stars = '*' * depth
|
||||
cookie = ' [/]' if tree_children.get(iid) else ''
|
||||
head = f'{stars} {kw} {title} ({kind}){cookie}'
|
||||
pad = max(1, 95 - len(head))
|
||||
out.append(f'{head}{" " * pad}{headline_tags(i, kind)}')
|
||||
|
||||
indent = ' ' * (depth - 1) + ' '
|
||||
out.append(f'{indent}:PROPERTIES:')
|
||||
out.append(f'{indent}:ID: {iid}')
|
||||
out.append(f'{indent}:TYPE: {i["issue_type"]}')
|
||||
out.append(f'{indent}:PRIORITY: P{i["priority"]}')
|
||||
out.append(f'{indent}:STATUS: {i["status"]}')
|
||||
if i.get('assignee'):
|
||||
out.append(f'{indent}:ASSIGNEE: {i["assignee"]}')
|
||||
if i.get('owner'):
|
||||
out.append(f'{indent}:OWNER: {i["owner"]}')
|
||||
if i.get('created_by'):
|
||||
out.append(f'{indent}:CREATED_BY: {i["created_by"]}')
|
||||
out.append(f'{indent}:CREATED: {fmt_date(i["created_at"])}')
|
||||
out.append(f'{indent}:UPDATED: {fmt_date(i["updated_at"])}')
|
||||
if i.get('comment_count'):
|
||||
out.append(f'{indent}:COMMENTS: {i["comment_count"]}')
|
||||
out.append(f'{indent}:KIND: {kind}')
|
||||
closed_deps = sorted(
|
||||
d['depends_on_id']
|
||||
for d in (i.get('dependencies') or [])
|
||||
if (ref := all_by_id.get(d['depends_on_id'])) and ref['status'] == 'closed'
|
||||
)
|
||||
if closed_deps:
|
||||
out.append(f'{indent}:DONE_DEPS: {", ".join(closed_deps)}')
|
||||
if iid in path:
|
||||
out.append(f'{indent}:CYCLE: yes — descendants pruned at this node')
|
||||
out.append(f'{indent}:END:')
|
||||
|
||||
if iid in path:
|
||||
return # cycle guard
|
||||
|
||||
details_stars = '*' * (depth + 1)
|
||||
section_stars = '*' * (depth + 2)
|
||||
section_indent = ' ' * (depth + 1) + ' '
|
||||
if any(i.get(k) for k in ('description', 'design', 'acceptance_criteria', 'notes')):
|
||||
out.append(f'{details_stars} Details')
|
||||
for label, key in [
|
||||
('Description', 'description'),
|
||||
('Design', 'design'),
|
||||
('Acceptance Criteria', 'acceptance_criteria'),
|
||||
('Notes', 'notes'),
|
||||
]:
|
||||
v = i.get(key)
|
||||
if not v:
|
||||
continue
|
||||
out.append(f'{section_stars} {label}')
|
||||
for line in v.rstrip('\n').splitlines():
|
||||
out.append(f'{section_indent}{line}' if line else '')
|
||||
|
||||
new_path = path | {iid}
|
||||
for c in sorted(tree_children.get(iid, set()), key=order_key):
|
||||
out.append('')
|
||||
emit(c, depth + 1, new_path)
|
||||
|
||||
for r in roots:
|
||||
emit(r, 1, frozenset())
|
||||
out.append('')
|
||||
|
||||
out_path.write_text('\n'.join(out))
|
||||
print(f'Wrote {out_path} ({len(out)} lines, {len(roots)} roots, {len(by_id)} issues)')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -118,6 +118,35 @@ mod tests {
|
||||
assert!(effects.is_empty());
|
||||
}
|
||||
|
||||
/// `CommitAndAdvance` must thread its `edit_mode` through to the
|
||||
/// trailing `EnterEditAtCursor` effect so the post-commit re-edit lands
|
||||
/// in the mode the keymap requested. The command never reads ctx.mode.
|
||||
#[test]
|
||||
fn commit_and_advance_threads_edit_mode_to_enter_edit_at_cursor() {
|
||||
let m = two_cat_model();
|
||||
let layout = make_layout(&m);
|
||||
let reg = make_registry();
|
||||
let mut bufs = HashMap::new();
|
||||
bufs.insert("edit".to_string(), "42".to_string());
|
||||
let mut ctx = make_ctx(&m, &layout, ®);
|
||||
ctx.buffers = &bufs;
|
||||
// ctx.mode stays Normal — the command must not look at it.
|
||||
let key = ctx.cell_key().unwrap();
|
||||
let cmd = CommitAndAdvance {
|
||||
key,
|
||||
value: "42".to_string(),
|
||||
advance: super::AdvanceDir::Down,
|
||||
cursor: super::CursorState::from_ctx(&ctx),
|
||||
edit_mode: AppMode::records_editing(),
|
||||
};
|
||||
let effects = cmd.execute(&ctx);
|
||||
let dbg = effects_debug(&effects);
|
||||
assert!(
|
||||
dbg.contains("EnterEditAtCursor") && dbg.contains("RecordsEditing"),
|
||||
"Expected trailing EnterEditAtCursor with RecordsEditing target, got: {dbg}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn commit_export_produces_export_and_normal_mode() {
|
||||
let m = two_cat_model();
|
||||
@ -234,12 +263,18 @@ pub enum AdvanceDir {
|
||||
|
||||
/// Commit a cell edit, advance the cursor, and re-enter edit mode.
|
||||
/// Subsumes the old `CommitCellEdit` (Down) and `CommitAndAdvanceRight` (Right).
|
||||
///
|
||||
/// `edit_mode` is the editing mode to re-enter after advancing. The keymap
|
||||
/// binding supplies this — the editing-mode keymap passes `editing` and the
|
||||
/// records-editing keymap passes `records-editing`. The command itself
|
||||
/// never inspects `ctx.mode`.
|
||||
#[derive(Debug)]
|
||||
pub struct CommitAndAdvance {
|
||||
pub key: CellKey,
|
||||
pub value: String,
|
||||
pub advance: AdvanceDir,
|
||||
pub cursor: CursorState,
|
||||
pub edit_mode: AppMode,
|
||||
}
|
||||
impl Cmd for CommitAndAdvance {
|
||||
fn name(&self) -> &'static str {
|
||||
@ -287,7 +322,9 @@ impl Cmd for CommitAndAdvance {
|
||||
}
|
||||
}
|
||||
}
|
||||
effects.push(Box::new(effect::EnterEditAtCursor));
|
||||
effects.push(Box::new(effect::EnterEditAtCursor {
|
||||
target_mode: self.edit_mode.clone(),
|
||||
}));
|
||||
effects
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,21 +10,6 @@ mod tests {
|
||||
use crate::command::cmd::test_helpers::*;
|
||||
use crate::workbook::Workbook;
|
||||
|
||||
#[test]
|
||||
fn enter_edit_mode_produces_editing_mode() {
|
||||
let m = two_cat_model();
|
||||
let layout = make_layout(&m);
|
||||
let reg = make_registry();
|
||||
let ctx = make_ctx(&m, &layout, ®);
|
||||
let cmd = EnterEditMode {
|
||||
initial_value: String::new(),
|
||||
};
|
||||
let effects = cmd.execute(&ctx);
|
||||
assert_eq!(effects.len(), 2);
|
||||
let dbg = format!("{:?}", effects[1]);
|
||||
assert!(dbg.contains("Editing"), "Expected Editing mode, got: {dbg}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enter_tile_select_with_categories() {
|
||||
let m = two_cat_model();
|
||||
@ -98,11 +83,80 @@ mod tests {
|
||||
let layout = make_layout(&m);
|
||||
let reg = make_registry();
|
||||
let ctx = make_ctx(&m, &layout, ®);
|
||||
let effects = EditOrDrill.execute(&ctx);
|
||||
let effects = EditOrDrill {
|
||||
edit_mode: AppMode::editing(),
|
||||
}
|
||||
.execute(&ctx);
|
||||
assert_eq!(effects.len(), 2);
|
||||
let dbg = effects_debug(&effects);
|
||||
assert!(dbg.contains("Editing"), "Expected Editing mode, got: {dbg}");
|
||||
}
|
||||
|
||||
/// EditOrDrill must trust its `edit_mode` parameter rather than checking
|
||||
/// `ctx.mode` — the records-normal keymap supplies `records-editing`,
|
||||
/// but the command itself never inspects the runtime mode. This is the
|
||||
/// parallel of the (deleted) `enter_edit_mode_produces_editing_mode`
|
||||
/// test for the records branch.
|
||||
#[test]
|
||||
fn edit_or_drill_passes_records_editing_mode_through() {
|
||||
let m = two_cat_model();
|
||||
let layout = make_layout(&m);
|
||||
let reg = make_registry();
|
||||
// Note: ctx.mode is still Normal here — the command must not look at it.
|
||||
let ctx = make_ctx(&m, &layout, ®);
|
||||
let effects = EditOrDrill {
|
||||
edit_mode: AppMode::records_editing(),
|
||||
}
|
||||
.execute(&ctx);
|
||||
assert_eq!(effects.len(), 2);
|
||||
let dbg = effects_debug(&effects);
|
||||
assert!(
|
||||
dbg.contains("RecordsEditing"),
|
||||
"Expected RecordsEditing mode, got: {dbg}"
|
||||
);
|
||||
}
|
||||
|
||||
/// `EnterEditAtCursorCmd` must hand its `target_mode` straight through
|
||||
/// to the `EnterEditAtCursor` effect — the keymap (records `o` sequence
|
||||
/// or commit-and-advance) decides; the command never inspects ctx.
|
||||
#[test]
|
||||
fn enter_edit_at_cursor_cmd_passes_target_mode_to_effect() {
|
||||
let m = two_cat_model();
|
||||
let layout = make_layout(&m);
|
||||
let reg = make_registry();
|
||||
let ctx = make_ctx(&m, &layout, ®);
|
||||
let effects = EnterEditAtCursorCmd {
|
||||
target_mode: AppMode::records_editing(),
|
||||
}
|
||||
.execute(&ctx);
|
||||
assert_eq!(effects.len(), 1);
|
||||
let dbg = format!("{:?}", effects[0]);
|
||||
assert!(
|
||||
dbg.contains("RecordsEditing"),
|
||||
"Expected RecordsEditing target_mode, got: {dbg}"
|
||||
);
|
||||
}
|
||||
|
||||
/// The edit branch pre-fills the `edit` buffer with the cell's current
|
||||
/// display value so the user can modify rather than retype.
|
||||
#[test]
|
||||
fn edit_or_drill_pre_fills_edit_buffer_with_display_value() {
|
||||
let m = two_cat_model();
|
||||
let layout = make_layout(&m);
|
||||
let reg = make_registry();
|
||||
let mut ctx = make_ctx(&m, &layout, ®);
|
||||
ctx.display_value = "42".to_string();
|
||||
let effects = EditOrDrill {
|
||||
edit_mode: AppMode::editing(),
|
||||
}
|
||||
.execute(&ctx);
|
||||
let dbg = effects_debug(&effects);
|
||||
assert!(
|
||||
dbg.contains("SetBuffer") && dbg.contains("\"edit\"") && dbg.contains("\"42\""),
|
||||
"Expected SetBuffer(\"edit\", \"42\"), got: {dbg}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enter_search_mode_sets_flag_and_clears_query() {
|
||||
let m = two_cat_model();
|
||||
@ -188,36 +242,18 @@ impl Cmd for SaveAndQuit {
|
||||
|
||||
// ── Editing entry ───────────────────────────────────────────────────────
|
||||
|
||||
/// Enter editing mode with an initial buffer value.
|
||||
#[derive(Debug)]
|
||||
pub struct EnterEditMode {
|
||||
pub initial_value: String,
|
||||
}
|
||||
impl Cmd for EnterEditMode {
|
||||
fn name(&self) -> &'static str {
|
||||
"enter-edit-mode"
|
||||
}
|
||||
fn execute(&self, ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
let edit_mode = if ctx.mode.is_records() {
|
||||
AppMode::records_editing()
|
||||
} else {
|
||||
AppMode::editing()
|
||||
};
|
||||
vec![
|
||||
Box::new(effect::SetBuffer {
|
||||
name: "edit".to_string(),
|
||||
value: self.initial_value.clone(),
|
||||
}),
|
||||
effect::change_mode(edit_mode),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/// Smart dispatch for i/a: if the cursor is on an aggregated pivot cell
|
||||
/// (categories on `Axis::None`, no records mode), drill into it instead of
|
||||
/// editing. Otherwise enter edit mode with the current displayed value.
|
||||
/// (categories on `Axis::None` and the cell is not a synthetic records-mode
|
||||
/// row), drill into it instead of editing. Otherwise pre-fill the edit
|
||||
/// buffer with the displayed cell value and enter `edit_mode`.
|
||||
///
|
||||
/// `edit_mode` is supplied by the keymap binding — the command itself is
|
||||
/// mode-agnostic, so the records-normal keymap passes `records-editing`
|
||||
/// while the normal keymap passes `editing`.
|
||||
#[derive(Debug)]
|
||||
pub struct EditOrDrill;
|
||||
pub struct EditOrDrill {
|
||||
pub edit_mode: AppMode,
|
||||
}
|
||||
impl Cmd for EditOrDrill {
|
||||
fn name(&self) -> &'static str {
|
||||
"edit-or-drill"
|
||||
@ -232,7 +268,8 @@ impl Cmd for EditOrDrill {
|
||||
.map(|cat| cat.kind.is_regular())
|
||||
.unwrap_or(false)
|
||||
});
|
||||
// In records mode (synthetic key), always edit directly — no drilling.
|
||||
// Synthetic records-mode cells are never aggregated — edit directly.
|
||||
// (This is a layout property, not a mode flag.)
|
||||
let is_synthetic = ctx.synthetic_record_at_cursor().is_some();
|
||||
let is_aggregated = !is_synthetic && regular_none;
|
||||
if is_aggregated {
|
||||
@ -241,23 +278,31 @@ impl Cmd for EditOrDrill {
|
||||
};
|
||||
return DrillIntoCell { key }.execute(ctx);
|
||||
}
|
||||
EnterEditMode {
|
||||
initial_value: ctx.display_value.clone(),
|
||||
}
|
||||
.execute(ctx)
|
||||
vec![
|
||||
Box::new(effect::SetBuffer {
|
||||
name: "edit".to_string(),
|
||||
value: ctx.display_value.clone(),
|
||||
}),
|
||||
effect::change_mode(self.edit_mode.clone()),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/// Thin command wrapper around the `EnterEditAtCursor` effect so it can
|
||||
/// participate in `Binding::Sequence`.
|
||||
/// participate in `Binding::Sequence`. `target_mode` is supplied as the
|
||||
/// command argument by the keymap binding.
|
||||
#[derive(Debug)]
|
||||
pub struct EnterEditAtCursorCmd;
|
||||
pub struct EnterEditAtCursorCmd {
|
||||
pub target_mode: AppMode,
|
||||
}
|
||||
impl Cmd for EnterEditAtCursorCmd {
|
||||
fn name(&self) -> &'static str {
|
||||
"enter-edit-at-cursor"
|
||||
}
|
||||
fn execute(&self, _ctx: &CmdContext) -> Vec<Box<dyn Effect>> {
|
||||
vec![Box::new(effect::EnterEditAtCursor)]
|
||||
vec![Box::new(effect::EnterEditAtCursor {
|
||||
target_mode: self.target_mode.clone(),
|
||||
})]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,27 @@ use crate::model::cell::CellKey;
|
||||
use crate::ui::app::AppMode;
|
||||
use crate::ui::effect::Panel;
|
||||
|
||||
/// Decode a mode-name string (as supplied by `enter-mode`/`edit-or-drill`
|
||||
/// keymap bindings) into an `AppMode`.
|
||||
fn parse_mode_name(s: &str) -> Result<AppMode, String> {
|
||||
match s {
|
||||
"normal" => Ok(AppMode::Normal),
|
||||
"help" => Ok(AppMode::Help),
|
||||
"formula-panel" => Ok(AppMode::FormulaPanel),
|
||||
"category-panel" => Ok(AppMode::CategoryPanel),
|
||||
"view-panel" => Ok(AppMode::ViewPanel),
|
||||
"tile-select" => Ok(AppMode::TileSelect),
|
||||
"command" => Ok(AppMode::command_mode()),
|
||||
"category-add" => Ok(AppMode::category_add()),
|
||||
"editing" => Ok(AppMode::editing()),
|
||||
"records-normal" => Ok(AppMode::RecordsNormal),
|
||||
"records-editing" => Ok(AppMode::records_editing()),
|
||||
"formula-edit" => Ok(AppMode::formula_edit()),
|
||||
"export-prompt" => Ok(AppMode::export_prompt()),
|
||||
other => Err(format!("Unknown mode: {other}")),
|
||||
}
|
||||
}
|
||||
|
||||
use super::cell::*;
|
||||
use super::commit::*;
|
||||
use super::core::*;
|
||||
@ -266,22 +287,16 @@ pub fn default_registry() -> CmdRegistry {
|
||||
r.register_nullary(|| Box::new(SaveAndQuit));
|
||||
r.register_nullary(|| Box::new(SaveCmd));
|
||||
r.register_nullary(|| Box::new(EnterSearchMode));
|
||||
r.register(
|
||||
&EnterEditMode {
|
||||
initial_value: String::new(),
|
||||
},
|
||||
|args| {
|
||||
let val = args.first().cloned().unwrap_or_default();
|
||||
Ok(Box::new(EnterEditMode { initial_value: val }))
|
||||
},
|
||||
|_args, ctx| {
|
||||
Ok(Box::new(EnterEditMode {
|
||||
initial_value: ctx.display_value.clone(),
|
||||
}))
|
||||
},
|
||||
);
|
||||
r.register_nullary(|| Box::new(EditOrDrill));
|
||||
r.register_nullary(|| Box::new(EnterEditAtCursorCmd));
|
||||
r.register_pure(&NamedCmd("edit-or-drill"), |args| {
|
||||
require_args("edit-or-drill", args, 1)?;
|
||||
let edit_mode = parse_mode_name(&args[0])?;
|
||||
Ok(Box::new(EditOrDrill { edit_mode }))
|
||||
});
|
||||
r.register_pure(&NamedCmd("enter-edit-at-cursor"), |args| {
|
||||
require_args("enter-edit-at-cursor", args, 1)?;
|
||||
let target_mode = parse_mode_name(&args[0])?;
|
||||
Ok(Box::new(EnterEditAtCursorCmd { target_mode }))
|
||||
});
|
||||
r.register_nullary(|| Box::new(EnterExportPrompt));
|
||||
r.register_nullary(|| Box::new(EnterFormulaEdit));
|
||||
r.register_nullary(|| Box::new(EnterTileSelect));
|
||||
@ -310,23 +325,7 @@ pub fn default_registry() -> CmdRegistry {
|
||||
);
|
||||
r.register_pure(&NamedCmd("enter-mode"), |args| {
|
||||
require_args("enter-mode", args, 1)?;
|
||||
let mode = match args[0].as_str() {
|
||||
"normal" => AppMode::Normal,
|
||||
"help" => AppMode::Help,
|
||||
"formula-panel" => AppMode::FormulaPanel,
|
||||
"category-panel" => AppMode::CategoryPanel,
|
||||
"view-panel" => AppMode::ViewPanel,
|
||||
"tile-select" => AppMode::TileSelect,
|
||||
"command" => AppMode::command_mode(),
|
||||
"category-add" => AppMode::category_add(),
|
||||
"editing" => AppMode::editing(),
|
||||
"records-normal" => AppMode::RecordsNormal,
|
||||
"records-editing" => AppMode::records_editing(),
|
||||
"formula-edit" => AppMode::formula_edit(),
|
||||
"export-prompt" => AppMode::export_prompt(),
|
||||
other => return Err(format!("Unknown mode: {other}")),
|
||||
};
|
||||
Ok(Box::new(EnterMode(mode)))
|
||||
Ok(Box::new(EnterMode(parse_mode_name(&args[0])?)))
|
||||
});
|
||||
|
||||
// ── Search ───────────────────────────────────────────────────────────
|
||||
@ -522,25 +521,33 @@ pub fn default_registry() -> CmdRegistry {
|
||||
r.register_nullary(|| Box::new(CommandModeBackspace));
|
||||
|
||||
// ── Commit ───────────────────────────────────────────────────────────
|
||||
// commit-cell-edit / commit-and-advance-right take a mode-name arg
|
||||
// (e.g. "editing" or "records-editing") as args[0]. The keymap supplies
|
||||
// it; the command never inspects ctx.mode.
|
||||
r.register(
|
||||
&CommitAndAdvance {
|
||||
key: CellKey::new(vec![]),
|
||||
value: String::new(),
|
||||
advance: AdvanceDir::Down,
|
||||
cursor: CursorState::default(),
|
||||
edit_mode: AppMode::editing(),
|
||||
},
|
||||
|args| {
|
||||
if args.len() < 2 {
|
||||
return Err("commit-cell-edit requires a value and coords".into());
|
||||
if args.len() < 3 {
|
||||
return Err("commit-cell-edit requires a mode, value, and coords".into());
|
||||
}
|
||||
let edit_mode = parse_mode_name(&args[0])?;
|
||||
Ok(Box::new(CommitAndAdvance {
|
||||
key: parse_cell_key_from_args(&args[1..]),
|
||||
value: args[0].clone(),
|
||||
key: parse_cell_key_from_args(&args[2..]),
|
||||
value: args[1].clone(),
|
||||
advance: AdvanceDir::Down,
|
||||
cursor: CursorState::default(),
|
||||
edit_mode,
|
||||
}))
|
||||
},
|
||||
|_args, ctx| {
|
||||
|args, ctx| {
|
||||
require_args("commit-cell-edit", args, 1)?;
|
||||
let edit_mode = parse_mode_name(&args[0])?;
|
||||
let value = read_buffer(ctx, "edit");
|
||||
let key = ctx.cell_key().clone().ok_or("no cell at cursor")?;
|
||||
Ok(Box::new(CommitAndAdvance {
|
||||
@ -548,6 +555,7 @@ pub fn default_registry() -> CmdRegistry {
|
||||
value,
|
||||
advance: AdvanceDir::Down,
|
||||
cursor: CursorState::from_ctx(ctx),
|
||||
edit_mode,
|
||||
}))
|
||||
},
|
||||
);
|
||||
@ -557,9 +565,12 @@ pub fn default_registry() -> CmdRegistry {
|
||||
value: String::new(),
|
||||
advance: AdvanceDir::Right,
|
||||
cursor: CursorState::default(),
|
||||
edit_mode: AppMode::editing(),
|
||||
},
|
||||
|_| Err("commit-and-advance-right requires context".into()),
|
||||
|_args, ctx| {
|
||||
|args, ctx| {
|
||||
require_args("commit-and-advance-right", args, 1)?;
|
||||
let edit_mode = parse_mode_name(&args[0])?;
|
||||
let value = read_buffer(ctx, "edit");
|
||||
let key = ctx.cell_key().clone().ok_or("no cell at cursor")?;
|
||||
Ok(Box::new(CommitAndAdvance {
|
||||
@ -567,6 +578,7 @@ pub fn default_registry() -> CmdRegistry {
|
||||
value,
|
||||
advance: AdvanceDir::Right,
|
||||
cursor: CursorState::from_ctx(ctx),
|
||||
edit_mode,
|
||||
}))
|
||||
},
|
||||
);
|
||||
|
||||
@ -431,9 +431,21 @@ impl KeymapSet {
|
||||
);
|
||||
normal.bind(KeyCode::Tab, none, "cycle-panel-focus");
|
||||
|
||||
// Editing entry — i/a drill into aggregated cells, else edit
|
||||
normal.bind(KeyCode::Char('i'), none, "edit-or-drill");
|
||||
normal.bind(KeyCode::Char('a'), none, "edit-or-drill");
|
||||
// Editing entry — i/a drill into aggregated cells, else edit.
|
||||
// The mode arg controls which editing mode is entered; records-normal
|
||||
// overrides these to "records-editing" via its own bindings.
|
||||
normal.bind_args(
|
||||
KeyCode::Char('i'),
|
||||
none,
|
||||
"edit-or-drill",
|
||||
vec!["editing".into()],
|
||||
);
|
||||
normal.bind_args(
|
||||
KeyCode::Char('a'),
|
||||
none,
|
||||
"edit-or-drill",
|
||||
vec!["editing".into()],
|
||||
);
|
||||
normal.bind(KeyCode::Enter, none, "enter-advance");
|
||||
normal.bind(KeyCode::Char('e'), ctrl, "enter-export-prompt");
|
||||
|
||||
@ -488,10 +500,27 @@ impl KeymapSet {
|
||||
|
||||
// ── Records normal mode (inherits from normal) ────────────────────
|
||||
let mut rn = Keymap::with_parent(normal);
|
||||
// Override i/a so the edit branch produces records-editing mode
|
||||
// instead of inheriting the normal-mode "editing" arg.
|
||||
rn.bind_args(
|
||||
KeyCode::Char('i'),
|
||||
none,
|
||||
"edit-or-drill",
|
||||
vec!["records-editing".into()],
|
||||
);
|
||||
rn.bind_args(
|
||||
KeyCode::Char('a'),
|
||||
none,
|
||||
"edit-or-drill",
|
||||
vec!["records-editing".into()],
|
||||
);
|
||||
rn.bind_seq(
|
||||
KeyCode::Char('o'),
|
||||
none,
|
||||
vec![("add-record-row", vec![]), ("enter-edit-at-cursor", vec![])],
|
||||
vec![
|
||||
("add-record-row", vec![]),
|
||||
("enter-edit-at-cursor", vec!["records-editing".into()]),
|
||||
],
|
||||
);
|
||||
set.insert(ModeKey::RecordsNormal, Arc::new(rn));
|
||||
|
||||
@ -736,6 +765,8 @@ impl KeymapSet {
|
||||
set.insert(ModeKey::TileSelect, Arc::new(ts));
|
||||
|
||||
// ── Editing mode ─────────────────────────────────────────────────
|
||||
// commit-* takes the target edit-mode arg so the command stays
|
||||
// mode-agnostic; records-editing overrides Enter/Tab below.
|
||||
let mut ed = Keymap::new();
|
||||
ed.bind_seq(
|
||||
KeyCode::Esc,
|
||||
@ -749,7 +780,7 @@ impl KeymapSet {
|
||||
KeyCode::Enter,
|
||||
none,
|
||||
vec![
|
||||
("commit-cell-edit", vec![]),
|
||||
("commit-cell-edit", vec!["editing".into()]),
|
||||
("clear-buffer", vec!["edit".into()]),
|
||||
],
|
||||
);
|
||||
@ -757,7 +788,7 @@ impl KeymapSet {
|
||||
KeyCode::Tab,
|
||||
none,
|
||||
vec![
|
||||
("commit-and-advance-right", vec![]),
|
||||
("commit-and-advance-right", vec!["editing".into()]),
|
||||
("clear-buffer", vec!["edit".into()]),
|
||||
],
|
||||
);
|
||||
@ -767,6 +798,7 @@ impl KeymapSet {
|
||||
set.insert(ModeKey::Editing, ed.clone());
|
||||
|
||||
// ── Records editing mode (inherits from editing) ──────────────────
|
||||
// Override Enter/Tab so the post-commit re-enter targets records-editing.
|
||||
let mut re = Keymap::with_parent(ed);
|
||||
re.bind_seq(
|
||||
KeyCode::Esc,
|
||||
@ -776,6 +808,22 @@ impl KeymapSet {
|
||||
("enter-mode", vec!["records-normal".into()]),
|
||||
],
|
||||
);
|
||||
re.bind_seq(
|
||||
KeyCode::Enter,
|
||||
none,
|
||||
vec![
|
||||
("commit-cell-edit", vec!["records-editing".into()]),
|
||||
("clear-buffer", vec!["edit".into()]),
|
||||
],
|
||||
);
|
||||
re.bind_seq(
|
||||
KeyCode::Tab,
|
||||
none,
|
||||
vec![
|
||||
("commit-and-advance-right", vec!["records-editing".into()]),
|
||||
("clear-buffer", vec!["edit".into()]),
|
||||
],
|
||||
);
|
||||
set.insert(ModeKey::RecordsEditing, Arc::new(re));
|
||||
|
||||
// ── Formula edit ─────────────────────────────────────────────────
|
||||
|
||||
@ -124,10 +124,19 @@ impl Effect for RemoveFormula {
|
||||
|
||||
/// Re-enter edit mode by reading the cell value at the current cursor.
|
||||
/// Used after commit+advance to continue data entry.
|
||||
///
|
||||
/// `target_mode` is supplied by the caller (keymap binding via
|
||||
/// `EnterEditAtCursorCmd`, or `CommitAndAdvance` from its own `edit_mode`
|
||||
/// field). The effect itself never inspects `app.mode` — the mode is decided
|
||||
/// statically by whoever invoked us.
|
||||
#[derive(Debug)]
|
||||
pub struct EnterEditAtCursor;
|
||||
pub struct EnterEditAtCursor {
|
||||
pub target_mode: AppMode,
|
||||
}
|
||||
impl Effect for EnterEditAtCursor {
|
||||
fn apply(&self, app: &mut App) {
|
||||
// Layout may be stale relative to prior effects in this batch (e.g.
|
||||
// AddRecordRow added a row); rebuild before reading display_value.
|
||||
app.rebuild_layout();
|
||||
let ctx = app.cmd_context(
|
||||
crossterm::event::KeyCode::Null,
|
||||
@ -136,11 +145,7 @@ impl Effect for EnterEditAtCursor {
|
||||
let value = ctx.display_value.clone();
|
||||
drop(ctx);
|
||||
app.buffers.insert("edit".to_string(), value);
|
||||
app.mode = if app.mode.is_records() {
|
||||
AppMode::records_editing()
|
||||
} else {
|
||||
AppMode::editing()
|
||||
};
|
||||
app.mode = self.target_mode.clone();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1288,6 +1293,41 @@ mod tests {
|
||||
assert_eq!(app.mode, AppMode::Help);
|
||||
}
|
||||
|
||||
/// `EnterEditAtCursor` must use its `target_mode` field, *not* whatever
|
||||
/// `app.mode` happens to be when applied. Previous implementation
|
||||
/// branched on `app.mode.is_records()` — the parameterized version
|
||||
/// trusts the caller (keymap or composing command).
|
||||
#[test]
|
||||
fn enter_edit_at_cursor_uses_target_mode_not_app_mode() {
|
||||
let mut app = test_app();
|
||||
// App starts in Normal mode — but caller has decided we want
|
||||
// RecordsEditing (e.g. records-mode `o` sequence).
|
||||
assert_eq!(app.mode, AppMode::Normal);
|
||||
EnterEditAtCursor {
|
||||
target_mode: AppMode::records_editing(),
|
||||
}
|
||||
.apply(&mut app);
|
||||
assert!(
|
||||
matches!(app.mode, AppMode::RecordsEditing { .. }),
|
||||
"Expected RecordsEditing, got {:?}",
|
||||
app.mode
|
||||
);
|
||||
|
||||
// Same effect with editing target — should land in plain Editing
|
||||
// even if app.mode was something else.
|
||||
let mut app2 = test_app();
|
||||
app2.mode = AppMode::RecordsNormal;
|
||||
EnterEditAtCursor {
|
||||
target_mode: AppMode::editing(),
|
||||
}
|
||||
.apply(&mut app2);
|
||||
assert!(
|
||||
matches!(app2.mode, AppMode::Editing { .. }),
|
||||
"Expected Editing, got {:?}",
|
||||
app2.mode
|
||||
);
|
||||
}
|
||||
|
||||
/// SetBuffer with empty value clears the buffer (used by clear-buffer command
|
||||
/// in keymap sequences after commit).
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user