v0.22.0 a85da87

search.go RawHistoryFolder

1package url
2
3import "strings"
4
5// ParseBangShortcut extracts a bang shortcut from input.
6// Input must start with "!" followed by shortcut key and a space.
7// Returns (shortcutKey, query, found).
8//
9// Examples:
10//
11//	"!g golang"      → ("g", "golang", true)
12//	"!ddg test"      → ("ddg", "test", true)
13//	"!gh repo name"  → ("gh", "repo name", true)
14//	"!g"             → ("", "", false) - no query
15//	"plain text"     → ("", "", false)
16//	"test !g"        → ("", "", false) - bang not at start
17func ParseBangShortcut(input string) (shortcut, query string, found bool) {
18	if !strings.HasPrefix(input, "!") {
19		return "", "", false
20	}
21
22	// Find the space separating shortcut from query
23	spaceIdx := strings.Index(input, " ")
24	if spaceIdx == -1 || spaceIdx == 1 {
25		// No space found or empty shortcut ("! query")
26		return "", "", false
27	}
28
29	shortcut = input[1:spaceIdx]
30	query = strings.TrimSpace(input[spaceIdx+1:])
31
32	if query == "" {
33		return "", "", false
34	}
35
36	return shortcut, query, true
37}
38
39// BuildSearchURL constructs a URL from user input, handling bang shortcuts.
40// It checks for bang shortcuts first, then URL-like input, then falls back to default search.
41//
42// Parameters:
43//   - input: user input (e.g., "!g golang", "example.com", "search query")
44//   - shortcutURLs: map of shortcut keys to URL templates (e.g., {"g": "https://google.com/search?q=%s"})
45//   - defaultSearch: default search engine URL template (e.g., "https://duckduckgo.com/?q=%s")
46//
47// Returns the resolved URL.
48func BuildSearchURL(input string, shortcutURLs map[string]string, defaultSearch string) string {
49	if input == "" {
50		return ""
51	}
52
53	// Check for bang shortcut (e.g., "!g query")
54	if shortcutKey, query, found := ParseBangShortcut(input); found {
55		if urlTemplate, ok := shortcutURLs[shortcutKey]; ok {
56			return strings.Replace(urlTemplate, "%s", query, 1)
57		}
58		// Unknown bang falls through to default search with original input
59	}
60
61	// Check if it looks like a URL
62	if LooksLikeURL(input) {
63		return Normalize(input)
64	}
65
66	// Use default search
67	if defaultSearch != "" {
68		return strings.Replace(defaultSearch, "%s", input, 1)
69	}
70
71	return input
72}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close