Skip to content

Commit 0e950b4

Browse files
committed
feat: improve search UX with smart std:: prefix and interactive menu
- Implement hybrid search strategy that tries std:: prefix first - Searches for std::<pattern> before falling back to original pattern - Returns std:: results early if >= 5 matches found - Combines both result sets if std:: results are sparse - Change default behavior to show first match directly - Reverts selection menu from default cppman <page> behavior - Provides faster access to most common use case - Move interactive selection menu to -f flag - cppman -f <keyword> now shows paginated selection menu - Menu displays 20 results per page - Navigation: 'n'/'next' for next page, 'p'/'prev' for previous - Enter number to select, Enter alone to cancel - Update documentation - Updated program help text to explain new behavior - Updated man page with smart search and pagination details - Added -n/--max-results option documentation This improves UX by reducing noise (e.g., 'cppman map' shows std::map directly) while still providing comprehensive search via -f flag.
1 parent 2948895 commit 0e950b4

3 files changed

Lines changed: 68 additions & 13 deletions

File tree

bin/cppman

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def main():
7070
help='Clear all cached files.'),
7171
make_option('-f', '--find-page', action='store', type='string',
7272
dest='keyword', default=None,
73-
help='Find man page.'),
73+
help='Find man page and show selection menu if multiple matches.'),
7474
make_option('-o', '--force-update', action='store_true',
7575
dest='force', default=False,
7676
help="Force cppman to update existing cache when "
@@ -97,7 +97,10 @@ def main():
9797
]
9898

9999
parser = OptionParser(
100-
usage='Usage: cppman [OPTION...] PAGE...', option_list=option_list)
100+
usage='Usage: cppman [OPTION...] PAGE...\n\n'
101+
'When PAGE is specified without -f, cppman shows the first match directly.\n'
102+
'Search automatically tries std:: prefix first for better results.',
103+
option_list=option_list)
101104

102105
options, args = parser.parse_args()
103106

@@ -118,7 +121,11 @@ def main():
118121

119122
if options.keyword:
120123
try:
121-
cm.find(options.keyword)
124+
keyword = cm.fuzzy_find(options.keyword, options.max_results, show_menu=True)
125+
if not keyword:
126+
sys.exit(1)
127+
pid = cm.man(keyword)
128+
os.waitpid(pid, 0)
122129
sys.exit(0)
123130
except RuntimeError as e:
124131
print(e, file=sys.stderr)
@@ -161,7 +168,7 @@ def main():
161168
sys.exit(1)
162169

163170
try:
164-
keyword = cm.fuzzy_find(args[0], options.max_results)
171+
keyword = cm.fuzzy_find(args[0], options.max_results, show_menu=False)
165172
if not keyword:
166173
sys.exit(1)
167174

cppman/main.py

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,21 @@ def _search_keyword(self, pattern):
537537
self.source = environ.source
538538

539539
self.cursor.execute('PRAGMA case_sensitive_like=ON')
540+
541+
std_results = []
542+
if not pattern.startswith('std::'):
543+
std_pattern = 'std::' + pattern
544+
std_results = self._fetch_page_by_keyword("%s" % std_pattern)
545+
std_results.extend(self._fetch_page_by_keyword("%s %%" % std_pattern))
546+
std_results.extend(self._fetch_page_by_keyword("%% %s" % std_pattern))
547+
std_results.extend(self._fetch_page_by_keyword("%% %s %%" % std_pattern))
548+
std_results.extend(self._fetch_page_by_keyword("%s%%" % std_pattern))
549+
std_results = list(set(std_results))
550+
551+
if len(std_results) >= 5:
552+
conn.close()
553+
return sorted(std_results, key=lambda e: _sort_search(e, std_pattern))
554+
540555
results = self._fetch_page_by_keyword("%s" % pattern)
541556
results.extend(self._fetch_page_by_keyword("%s %%" % pattern))
542557
results.extend(self._fetch_page_by_keyword("%% %s" % pattern))
@@ -546,6 +561,8 @@ def _search_keyword(self, pattern):
546561
if len(results) == 0:
547562
results = self._fetch_page_by_keyword("%%%s%%" % pattern)
548563

564+
results.extend(std_results)
565+
549566
conn.close()
550567
return sorted(list(set(results)), key=lambda e: _sort_search(e, pattern))
551568

@@ -596,8 +613,8 @@ def find(self, pattern):
596613
else:
597614
raise RuntimeError('%s: nothing appropriate.' % pattern)
598615

599-
def fuzzy_find(self, pattern, max_results):
600-
"""Find pages in database and present an interactive selection menu."""
616+
def fuzzy_find(self, pattern, max_results, show_menu=False):
617+
"""Find pages in database and optionally present an interactive selection menu."""
601618
results = self._search_keyword(pattern)
602619
if max_results >= 1:
603620
results = results[:max_results]
@@ -608,21 +625,48 @@ def fuzzy_find(self, pattern, max_results):
608625
if len(results) == 1:
609626
return results[0][1]
610627

611-
for i, (name, keyword, url) in enumerate(results, 1):
612-
print(f"{i}. {keyword} - {name}")
628+
if not show_menu:
629+
return results[0][1]
630+
631+
page_size = 20
632+
current_page = 0
633+
total_pages = (len(results) + page_size - 1) // page_size
613634

614635
while True:
636+
start_idx = current_page * page_size
637+
end_idx = min(start_idx + page_size, len(results))
638+
639+
print(f"\n--- Page {current_page + 1}/{total_pages} ---")
640+
for i in range(start_idx, end_idx):
641+
name, keyword, url = results[i]
642+
print(f"{i + 1}. {keyword} - {name}")
643+
644+
if total_pages > 1:
645+
nav_help = []
646+
if current_page > 0:
647+
nav_help.append("'p' for previous")
648+
if current_page < total_pages - 1:
649+
nav_help.append("'n' for next")
650+
print(f"\nEnter number to select, {', '.join(nav_help)}, or press Enter to cancel")
651+
615652
try:
616-
selection = input("\nPlease enter the selection: ")
653+
selection = input("\nPlease enter the selection: ").strip().lower()
617654
if not selection:
618655
return None
619656

657+
if selection in ('n', 'next') and current_page < total_pages - 1:
658+
current_page += 1
659+
continue
660+
elif selection in ('p', 'prev', 'previous') and current_page > 0:
661+
current_page -= 1
662+
continue
663+
620664
idx = int(selection) - 1
621665
if 0 <= idx < len(results):
622666
return results[idx][1]
623667
print("Invalid selection. Please try again.")
624668
except ValueError:
625-
print("Please enter a valid number.")
669+
print("Please enter a valid number or navigation command.")
626670
except KeyboardInterrupt:
627671
print("\nOperation cancelled.")
628672
return None

misc/cppman.1

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ cppman - C++ manual page viewer / fetcher
66
.I OPTIONS...
77
.B ] PAGE...
88
.SH DESCRIPTION
9-
cppman generates C++ manual pages from cplusplus.com and provide a man\-like interface to view man pages.
9+
cppman generates C++ manual pages from cplusplus.com and cppreference.com and provides a man\-like interface to view man pages.
1010
.sp
1111
By default, cppman fetches man pages on-the-fly, by running the command 'cppman \-c', all available manpages are cached, making offline browsing possible. This is also required if you want to use the system 'man' command.
12+
.sp
13+
When searching for a page (e.g., 'cppman map'), cppman automatically tries the std:: prefix first (std::map) for better results. If a page name is specified without the \-f option, cppman displays the first match directly. Use \-f to show an interactive selection menu with pagination support (20 items per page).
1214
.SS Browsing man pages
1315
cppman uses Vi Improved as a pager.
1416
.br
@@ -24,7 +26,7 @@ cache all available man pages from cplusplus.com to enable offline browsing
2426
.IP "\-C, \-\-clear\-cache"
2527
clear all cached files
2628
.IP "\-f KEYWORD, \-\-find\-page=KEYWORD"
27-
find man page
29+
find man page and show interactive selection menu if multiple matches are found. The menu displays up to 20 results per page. Use 'n' or 'next' to go to the next page, 'p' or 'prev' to go to the previous page, or enter a number to select a specific page.
2830
.IP "\-o, \-\-force\-update"
2931
force cppman to update existing cache when '\-\-cache\-all' or browsing man pages that were already cached
3032
.IP "\-m MANDB, \-\-use\-mandb=MANDB"
@@ -33,7 +35,9 @@ Accepts 'true' or 'false'. If true, cppman adds manpage path to mandb so that yo
3335
Select pager to use, accepts 'vim', 'nvim' or 'less'. The default value is 'vim'.
3436
If 'nvim' is selected, but not available, 'vim' is used as a fallback and vice versa. If either is selected, but neither is available, 'less' is used as a fallback.
3537
.IP "\-r, \-\-rebuild\-index"
36-
rebuild index database from cplusplus.com
38+
rebuild index database from cplusplus.com and cppreference.com
39+
.IP "\-n NUM, \-\-max\-results=NUM"
40+
maximum number of search results to show in the selection menu
3741
.IP "\-v, \-\-version"
3842
show version information
3943
.IP "\-h, \-\-help"

0 commit comments

Comments
 (0)