v0.6.0 5c50dd3

types.go RawHistoryFolder

1// Package parser provides URL parsing and fuzzy finding functionality for dumber.
2package parser
3
4import (
5	"time"
6
7	"github.com/bnema/dumber/internal/config"
8	"github.com/bnema/dumber/internal/db"
9)
10
11// InputType represents the type of user input detected.
12type InputType int
13
14const (
15	// InputTypeDirectURL represents a direct URL input (e.g., "reddit.com", "https://example.com")
16	InputTypeDirectURL InputType = iota
17	// InputTypeSearchShortcut represents a search shortcut (e.g., "g: golang tutorial")
18	InputTypeSearchShortcut
19	// InputTypeHistorySearch represents a history search query
20	InputTypeHistorySearch
21	// InputTypeFallbackSearch represents a fallback web search
22	InputTypeFallbackSearch
23)
24
25// ParseResult represents the result of parsing user input.
26type ParseResult struct {
27	// Type indicates the detected input type
28	Type InputType `json:"type"`
29	// URL is the final URL to navigate to
30	URL string `json:"url"`
31	// Query is the original user query
32	Query string `json:"query"`
33	// Confidence is the confidence score of the parsing (0.0-1.0)
34	Confidence float64 `json:"confidence"`
35	// FuzzyMatches contains fuzzy search results if applicable
36	FuzzyMatches []FuzzyMatch `json:"fuzzy_matches,omitempty"`
37	// Shortcut contains the detected shortcut information
38	Shortcut *DetectedShortcut `json:"shortcut,omitempty"`
39	// ProcessingTime is the time taken to process the input
40	ProcessingTime time.Duration `json:"processing_time"`
41}
42
43// DetectedShortcut represents a detected search shortcut.
44type DetectedShortcut struct {
45	// Key is the shortcut key (e.g., "g", "gh")
46	Key string `json:"key"`
47	// Query is the search query part
48	Query string `json:"query"`
49	// URL is the constructed URL
50	URL string `json:"url"`
51	// Description is the shortcut description
52	Description string `json:"description"`
53}
54
55// FuzzyMatch represents a fuzzy search match from history.
56type FuzzyMatch struct {
57	// HistoryEntry is the matched history entry
58	HistoryEntry *db.History `json:"history_entry"`
59	// Score is the similarity score (0.0-1.0, higher is better)
60	Score float64 `json:"score"`
61	// URLScore is the URL similarity score
62	URLScore float64 `json:"url_score"`
63	// TitleScore is the title similarity score
64	TitleScore float64 `json:"title_score"`
65	// RecencyScore is the recency score based on last visit
66	RecencyScore float64 `json:"recency_score"`
67	// VisitScore is the score based on visit count
68	VisitScore float64 `json:"visit_score"`
69	// MatchedField indicates which field matched (url, title, or both)
70	MatchedField string `json:"matched_field"`
71}
72
73// HistoryProvider defines the interface for history data access.
74type HistoryProvider interface {
75	// GetRecentHistory returns recent history entries limited by count
76	GetRecentHistory(limit int) ([]*db.History, error)
77	// GetAllHistory returns all history entries for fuzzy search
78	GetAllHistory() ([]*db.History, error)
79	// SearchHistory performs a basic text search in history
80	SearchHistory(query string, limit int) ([]*db.History, error)
81	// GetHistoryByURL retrieves history entry by exact URL match
82	GetHistoryByURL(url string) (*db.History, error)
83}
84
85// FuzzyConfig holds configuration for fuzzy matching algorithms.
86type FuzzyConfig struct {
87	// MinSimilarityThreshold is the minimum similarity score to consider a match (0.0-1.0)
88	MinSimilarityThreshold float64 `json:"min_similarity_threshold"`
89	// MaxResults is the maximum number of fuzzy results to return
90	MaxResults int `json:"max_results"`
91	// URLWeight is the weight for URL similarity in scoring
92	URLWeight float64 `json:"url_weight"`
93	// TitleWeight is the weight for title similarity in scoring
94	TitleWeight float64 `json:"title_weight"`
95	// RecencyWeight is the weight for recency in scoring
96	RecencyWeight float64 `json:"recency_weight"`
97	// VisitWeight is the weight for visit count in scoring
98	VisitWeight float64 `json:"visit_weight"`
99	// RecencyDecayDays is the number of days for recency score decay
100	RecencyDecayDays int `json:"recency_decay_days"`
101}
102
103// Parser handles URL parsing and fuzzy finding operations.
104type Parser struct {
105	config          *config.Config
106	fuzzyConfig     *FuzzyConfig
107	historyProvider HistoryProvider
108}
109
110// ParserOption is a functional option for configuring the Parser.
111type ParserOption func(*Parser)
112
113// NewParser creates a new Parser instance with the given configuration.
114func NewParser(cfg *config.Config, historyProvider HistoryProvider, opts ...ParserOption) *Parser {
115	p := &Parser{
116		config:          cfg,
117		historyProvider: historyProvider,
118		fuzzyConfig:     DefaultFuzzyConfig(),
119	}
120
121	for _, opt := range opts {
122		opt(p)
123	}
124
125	return p
126}
127
128// WithFuzzyConfig sets custom fuzzy matching configuration.
129func WithFuzzyConfig(fuzzyConfig *FuzzyConfig) ParserOption {
130	return func(p *Parser) {
131		p.fuzzyConfig = fuzzyConfig
132	}
133}
134
135// DefaultFuzzyConfig returns default fuzzy matching configuration.
136func DefaultFuzzyConfig() *FuzzyConfig {
137	return &FuzzyConfig{
138		MinSimilarityThreshold: 0.3,
139		MaxResults:             10,
140		URLWeight:              0.4,
141		TitleWeight:            0.3,
142		RecencyWeight:          0.2,
143		VisitWeight:            0.1,
144		RecencyDecayDays:       30,
145	}
146}
147
148// String returns a string representation of InputType.
149func (t InputType) String() string {
150	switch t {
151	case InputTypeDirectURL:
152		return "direct_url"
153	case InputTypeSearchShortcut:
154		return "search_shortcut"
155	case InputTypeHistorySearch:
156		return "history_search"
157	case InputTypeFallbackSearch:
158		return "fallback_search"
159	default:
160		return "unknown"
161	}
162}
163
164// IsValid returns true if the FuzzyConfig has valid values.
165func (fc *FuzzyConfig) IsValid() bool {
166	return fc.MinSimilarityThreshold >= 0.0 && fc.MinSimilarityThreshold <= 1.0 &&
167		fc.MaxResults > 0 &&
168		fc.URLWeight >= 0.0 && fc.TitleWeight >= 0.0 &&
169		fc.RecencyWeight >= 0.0 && fc.VisitWeight >= 0.0 &&
170		fc.RecencyDecayDays > 0
171}

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