1// Package autocomplete provides domain types and logic for URL autocompletion.
2package autocomplete
3
4import "strings"
5
6// SuggestionSource indicates the origin of an autocomplete suggestion.
7type SuggestionSource int
8
9const (
10 SourceHistory SuggestionSource = iota
11 SourceFavorite
12 SourceBangShortcut
13)
14
15// Suggestion represents a single autocomplete suggestion with its completion suffix.
16type Suggestion struct {
17 FullText string // The complete text (URL or bang shortcut)
18 Suffix string // The suffix to append after the user's input
19 Source SuggestionSource // Where this suggestion came from
20 Title string // Display title (page title or description)
21}
22
23// ComputeCompletionSuffix returns the suffix if input is a case-insensitive prefix of fullText.
24// Returns the suffix and true if input matches as a prefix, otherwise empty string and false.
25func ComputeCompletionSuffix(input, fullText string) (string, bool) {
26 if input == "" || fullText == "" {
27 return "", false
28 }
29
30 inputLower := strings.ToLower(input)
31 fullLower := strings.ToLower(fullText)
32
33 if !strings.HasPrefix(fullLower, inputLower) {
34 return "", false
35 }
36
37 // Return the original-case suffix from fullText
38 suffix := fullText[len(input):]
39 return suffix, suffix != ""
40}
41
42// StripProtocol removes http:// or https:// prefix from a URL for matching.
43func StripProtocol(url string) string {
44 if strings.HasPrefix(url, "https://") {
45 return url[8:]
46 }
47 if strings.HasPrefix(url, "http://") {
48 return url[7:]
49 }
50 return url
51}
52
53// ComputeURLCompletionSuffix computes the completion suffix for URLs,
54// handling protocol stripping for better matching.
55// It tries matching with and without protocol prefixes.
56func ComputeURLCompletionSuffix(input, fullURL string) (suffix, matchedURL string, ok bool) {
57 // First try direct match
58 if suffix, ok := ComputeCompletionSuffix(input, fullURL); ok {
59 return suffix, fullURL, true
60 }
61
62 // Try matching against URL without protocol
63 strippedURL := StripProtocol(fullURL)
64 if suffix, ok := ComputeCompletionSuffix(input, strippedURL); ok {
65 return suffix, strippedURL, true
66 }
67
68 // Try matching if input has www. but URL doesn't (or vice versa)
69 inputNoWWW := strings.TrimPrefix(input, "www.")
70 strippedNoWWW := strings.TrimPrefix(strippedURL, "www.")
71 if suffix, ok := ComputeCompletionSuffix(inputNoWWW, strippedNoWWW); ok {
72 // Return suffix relative to input
73 if strings.HasPrefix(strings.ToLower(strippedURL), strings.ToLower(input)) {
74 return strippedURL[len(input):], strippedURL, true
75 }
76 return suffix, strippedNoWWW, true
77 }
78
79 return "", "", false
80}