@@ -6,8 +6,11 @@ from collections import defaultdict
6
6
from pathlib import Path , PurePath
7
7
from typing import Dict , List , Tuple
8
8
9
+ from typing_extensions import Literal
10
+
9
11
from zulipterminal .config .keys import (
10
12
HELP_CATEGORIES ,
13
+ HELP_CONTEXTS ,
11
14
KEY_BINDINGS ,
12
15
display_keys_for_command ,
13
16
)
@@ -17,34 +20,62 @@ KEYS_FILE = (
17
20
Path (__file__ ).resolve ().parent .parent / "zulipterminal" / "config" / "keys.py"
18
21
)
19
22
KEYS_FILE_NAME = KEYS_FILE .name
20
- OUTPUT_FILE = Path (__file__ ).resolve ().parent .parent / "docs" / "hotkeys.md"
21
- OUTPUT_FILE_NAME = OUTPUT_FILE .name
23
+ HOTKEYS_GROUPED_BY_CATEGORIES_FILE = (
24
+ Path (__file__ ).resolve ().parent .parent / "docs" / "hotkeys.md"
25
+ )
26
+ HOTKEYS_GROUPED_BY_CATEGORIES_FILE_NAME = HOTKEYS_GROUPED_BY_CATEGORIES_FILE .name
27
+ HOTKEYS_GROUPED_BY_CONTEXTS_FILE = (
28
+ Path (__file__ ).resolve ().parent .parent / "docs" / "hotkeys-by-context.md"
29
+ )
30
+ HOTKEYS_GROUPED_BY_CONTEXTS_FILE_NAME = HOTKEYS_GROUPED_BY_CONTEXTS_FILE .name
22
31
SCRIPT_NAME = PurePath (__file__ ).name
23
32
HELP_TEXT_STYLE = re .compile (r"^[a-zA-Z /()',&@#:_-]*$" )
24
33
25
34
# Exclude keys from duplicate keys checking
26
35
KEYS_TO_EXCLUDE = ["q" , "e" , "m" , "r" ]
27
36
28
37
29
- def main (fix : bool ) -> None :
38
+ def main (fix : bool , context : bool ) -> None :
39
+ """
40
+ Handles the arguments and calls the respective functions
41
+ """
30
42
if fix :
31
- generate_hotkeys_file ()
32
- else :
33
- lint_hotkeys_file ()
43
+ generate_hotkeys_file (
44
+ help_groups = HELP_CATEGORIES ,
45
+ grouped_keys = read_help_groups ("key_category" ),
46
+ output_file = HOTKEYS_GROUPED_BY_CATEGORIES_FILE ,
47
+ output_file_name = HOTKEYS_GROUPED_BY_CATEGORIES_FILE_NAME ,
48
+ grouping = "category" ,
49
+ )
50
+ if context :
51
+ generate_hotkeys_file (
52
+ help_groups = HELP_CONTEXTS ,
53
+ grouped_keys = read_help_groups ("key_context" ),
54
+ output_file = HOTKEYS_GROUPED_BY_CONTEXTS_FILE ,
55
+ output_file_name = HOTKEYS_GROUPED_BY_CONTEXTS_FILE_NAME ,
56
+ grouping = "context" ,
57
+ )
58
+ if not fix and not context :
59
+ lint_hotkeys_file (
60
+ grouped_keys_by_category = read_help_groups ("key_category" ),
61
+ grouped_keys_by_context = read_help_groups ("key_context" ),
62
+ output_file = HOTKEYS_GROUPED_BY_CATEGORIES_FILE ,
63
+ output_file_name = HOTKEYS_GROUPED_BY_CATEGORIES_FILE_NAME ,
64
+ )
34
65
35
66
36
- def lint_hotkeys_file () -> None :
67
+ def lint_hotkeys_by_group (
68
+ grouped_keys : Dict [str , List [Tuple [str , List [str ]]]],
69
+ help_groups : Dict [str , str ],
70
+ grouping : str ,
71
+ ) -> int :
37
72
"""
38
- Lint KEYS_FILE for key description, then compare if in sync with
39
- existing OUTPUT_FILE
73
+ Lint KEYS_FILE for key description and key combinations
40
74
"""
41
- hotkeys_file_string = get_hotkeys_file_string ()
42
- # To lint keys description
43
- error_flag = 0
44
- categories = read_help_categories ()
45
- for action in HELP_CATEGORIES :
75
+ error_flag : int = 0
76
+ for action in help_groups :
46
77
check_duplicate_keys_list : List [str ] = []
47
- for help_text , key_combinations_list in categories [action ]:
78
+ for help_text , key_combinations_list in grouped_keys [action ]:
48
79
check_duplicate_keys_list .extend (key_combinations_list )
49
80
various_key_combinations = " / " .join (key_combinations_list )
50
81
# Check description style
@@ -65,49 +96,85 @@ def lint_hotkeys_file() -> None:
65
96
]
66
97
if len (duplicate_keys ) != 0 :
67
98
print (
68
- f"Duplicate key combination for keys { duplicate_keys } for category ( { HELP_CATEGORIES [action ]} ) detected"
99
+ f"Duplicate key combination for keys { duplicate_keys } for ( { grouping } : { help_groups [action ]} ) detected"
69
100
)
70
101
error_flag = 1
102
+ return error_flag
103
+
104
+
105
+ def lint_hotkeys_file (
106
+ grouped_keys_by_category : Dict [str , List [Tuple [str , List [str ]]]],
107
+ grouped_keys_by_context : Dict [str , List [Tuple [str , List [str ]]]],
108
+ output_file : Path ,
109
+ output_file_name : str ,
110
+ ) -> None :
111
+ """
112
+ Lint KEYS_FILE for key descriptions, category and context.
113
+ Then compare if in sync with existing OUTPUT_FILE.
114
+ """
115
+ error_flag : int = 0
116
+ error_flag |= lint_hotkeys_by_group (
117
+ grouped_keys_by_category , HELP_CATEGORIES , "category"
118
+ )
119
+ error_flag |= lint_hotkeys_by_group (
120
+ grouped_keys_by_context , HELP_CONTEXTS , "context"
121
+ )
71
122
if error_flag == 1 :
72
123
print (f"Rerun this command after resolving errors in config/{ KEYS_FILE_NAME } " )
73
124
else :
74
125
print ("No hotkeys linting errors" )
75
- if not output_file_matches_string (hotkeys_file_string ):
126
+ hotkeys_file_string = get_hotkeys_file_string (
127
+ HELP_CATEGORIES , grouped_keys_by_category
128
+ )
129
+ if not output_file_matches_string (
130
+ hotkeys_file_string , output_file , output_file_name
131
+ ):
76
132
print (
77
- f"Run './tools/{ SCRIPT_NAME } --fix' to update { OUTPUT_FILE_NAME } file"
133
+ f"Run './tools/{ SCRIPT_NAME } --fix' to update { output_file_name } file"
78
134
)
79
135
error_flag = 1
80
136
sys .exit (error_flag )
81
137
82
138
83
- def generate_hotkeys_file () -> None :
139
+ def generate_hotkeys_file (
140
+ help_groups : Dict [str , str ],
141
+ grouped_keys : Dict [str , List [Tuple [str , List [str ]]]],
142
+ output_file : Path ,
143
+ output_file_name : str ,
144
+ grouping : str ,
145
+ ) -> None :
84
146
"""
85
147
Generate OUTPUT_FILE based on help text description and
86
148
shortcut key combinations in KEYS_FILE
87
149
"""
88
- hotkeys_file_string = get_hotkeys_file_string ()
89
- output_file_matches_string (hotkeys_file_string )
90
- write_hotkeys_file (hotkeys_file_string )
91
- print (f"Hot Keys list saved in { OUTPUT_FILE } " )
150
+ hotkeys_file_string = get_hotkeys_file_string (help_groups , grouped_keys )
151
+ output_file_matches_string (hotkeys_file_string , output_file , output_file_name )
152
+ write_hotkeys_file (hotkeys_file_string , output_file )
153
+ if grouping == "category" :
154
+ print (f"Hotkeys list saved in { output_file } " )
155
+ else :
156
+ print (f"Hotkeys list grouped by { grouping } saved in { output_file } " )
92
157
93
158
94
- def get_hotkeys_file_string () -> str :
159
+ def get_hotkeys_file_string (
160
+ help_groups : Dict [str , str ],
161
+ grouped_keys : Dict [str , List [Tuple [str , List [str ]]]],
162
+ ) -> str :
95
163
"""
96
164
Construct string in form for output to OUTPUT_FILE based on help text
97
165
description and shortcut key combinations in KEYS_FILE
98
166
"""
99
- categories = read_help_categories ()
100
167
hotkeys_file_string = (
101
168
f"<!--- Generated automatically by tools/{ SCRIPT_NAME } -->\n "
102
169
"<!--- Do not modify -->\n \n # Hot Keys\n "
103
170
)
104
- for action in HELP_CATEGORIES :
171
+ for group in help_groups :
105
172
hotkeys_file_string += (
106
- f"## { HELP_CATEGORIES [ action ]} \n "
173
+ f"## { help_groups [ group ]} \n "
107
174
"|Command|Key Combination|\n "
108
175
"| :--- | :---: |\n "
109
176
)
110
- for help_text , key_combinations_list in categories [ action ]:
177
+ for help_text , key_combinations_list in grouped_keys [ group ]:
111
178
various_key_combinations = " / " .join (
112
179
[
113
180
" + " .join ([f"<kbd>{ key } </kbd>" for key in key_combination .split ()])
@@ -119,34 +186,48 @@ def get_hotkeys_file_string() -> str:
119
186
return hotkeys_file_string
120
187
121
188
122
- def output_file_matches_string (hotkeys_file_string : str ) -> bool :
123
- with open (OUTPUT_FILE ) as output_file :
124
- content_is_identical = hotkeys_file_string == output_file .read ()
125
- if content_is_identical :
126
- print (f"{ OUTPUT_FILE_NAME } file already in sync with config/{ KEYS_FILE_NAME } " )
127
- return True
128
- else :
129
- print (f"{ OUTPUT_FILE_NAME } file not in sync with config/{ KEYS_FILE_NAME } " )
189
+ def output_file_matches_string (
190
+ hotkeys_file_string : str , output_file : Path , output_file_name : str
191
+ ) -> bool :
192
+ """
193
+ Read the content of the existing OUTPUT_FILE as a string
194
+ and check if it matches the newly generated hotkeys_file_string
195
+ """
196
+ try :
197
+ with open (output_file ) as output_file_object :
198
+ content_is_identical = hotkeys_file_string == output_file_object .read ()
199
+ if content_is_identical :
200
+ print (
201
+ f"{ output_file_name } file already in sync with config/{ KEYS_FILE_NAME } "
202
+ )
203
+ return True
204
+ else :
205
+ print (f"{ output_file_name } file not in sync with config/{ KEYS_FILE_NAME } " )
206
+ return False
207
+ except FileNotFoundError :
208
+ print (f"{ output_file_name } does not exist" )
130
209
return False
131
210
132
211
133
- def read_help_categories () -> Dict [str , List [Tuple [str , List [str ]]]]:
212
+ def read_help_groups (
213
+ key_group : Literal ["key_category" , "key_context" ]
214
+ ) -> Dict [str , List [Tuple [str , List [str ]]]]:
134
215
"""
135
- Get all help categories from KEYS_FILE
216
+ Get all help keys grouped by key_group ( categories / contexts) from KEYS_FILE
136
217
"""
137
- categories = defaultdict (list )
218
+ grouped_keys = defaultdict (list )
138
219
for cmd , item in KEY_BINDINGS .items ():
139
- categories [item ["key_category" ]].append (
220
+ grouped_keys [item [key_group ]].append (
140
221
(item ["help_text" ], display_keys_for_command (cmd ))
141
222
)
142
- return categories
223
+ return grouped_keys
143
224
144
225
145
- def write_hotkeys_file (hotkeys_file_string : str ) -> None :
226
+ def write_hotkeys_file (hotkeys_file_string : str , output_file : Path ) -> None :
146
227
"""
147
- Write hotkeys_file_string variable once to OUTPUT_FILE
228
+ Write hotkeys_file_string variable once to the target output file
148
229
"""
149
- with open (OUTPUT_FILE , "w" ) as hotkeys_file :
230
+ with open (output_file , "w" ) as hotkeys_file :
150
231
hotkeys_file .write (hotkeys_file_string )
151
232
152
233
@@ -158,8 +239,14 @@ if __name__ == "__main__":
158
239
parser .add_argument (
159
240
"--fix" ,
160
241
action = "store_true" ,
161
- help = f"Generate { OUTPUT_FILE_NAME } file by extracting key description and key "
242
+ help = f"Generate { HOTKEYS_GROUPED_BY_CATEGORIES_FILE_NAME } file by extracting key description and key "
243
+ f"combination from config/{ KEYS_FILE_NAME } file" ,
244
+ )
245
+ parser .add_argument (
246
+ "--context" ,
247
+ action = "store_true" ,
248
+ help = f"Generate { HOTKEYS_GROUPED_BY_CONTEXTS_FILE_NAME } file by extracting key description and key "
162
249
f"combination from config/{ KEYS_FILE_NAME } file" ,
163
250
)
164
251
args = parser .parse_args ()
165
- main (args .fix )
252
+ main (args .fix , args . context )
0 commit comments