Documentation
¶
Overview ¶
Package watch provides file-system watching with debounced sync callbacks. Ported from src/sync/watcher.ts and src/sync/watch-policy.ts of github.com/colbymchenry/codegraph (MIT).
Index ¶
- Constants
- func DetectWSL() bool
- func EmitEventForTests(root, relPath string) bool
- func IsLockUnavailableError(err error) bool
- func ResetWSLCacheForTests()
- func WatchDisabledReason(projectRoot string, probe WatchProbe) string
- type DirtyState
- type FileWatcher
- func (fw *FileWatcher) DirtyState() DirtyState
- func (fw *FileWatcher) Drain(ctx context.Context) error
- func (fw *FileWatcher) FatalErrors() <-chan error
- func (fw *FileWatcher) IngestEventForTests(relPath string)
- func (fw *FileWatcher) IsActive() bool
- func (fw *FileWatcher) PendingFiles() []PendingFile
- func (fw *FileWatcher) Start() bool
- func (fw *FileWatcher) StartWithError() error
- func (fw *FileWatcher) Stop()
- func (fw *FileWatcher) StopAndWait()
- func (fw *FileWatcher) WaitUntilReady(timeout time.Duration) error
- type IsIgnoredFunc
- type IsSourceFileFunc
- type LockUnavailableError
- type NowFunc
- type Observation
- type ObservationKind
- type Options
- type PendingFile
- type SyncFunc
- type SyncPathsFunc
- type SyncResult
- type WatchProbe
Constants ¶
const DefaultDebounceMS = 2000
DefaultDebounceMS is the default debounce delay before a sync is triggered after the last file-change event (2000 ms, matching the original).
const DefaultMaxBatchEvents = 2_048
DefaultMaxBatchEvents bounds path-level tracking within one unresolved reconciliation generation. Once exceeded, one whole-worktree marker replaces the path set until a successful full reconciliation catches up.
const DefaultMaxDirWatches = 50_000
DefaultMaxDirWatches caps the number of simultaneously-watched directories on Linux (per-directory inotify path). Matches the original's 50 000.
Variables ¶
This section is empty.
Functions ¶
func DetectWSL ¶
func DetectWSL() bool
DetectWSL reports whether the current process is running under WSL. The result is cached after the first call.
func EmitEventForTests ¶
EmitEventForTests feeds a synthetic event to the live watcher registered for root. Returns false if no watcher is registered. For use in tests only.
func IsLockUnavailableError ¶
IsLockUnavailableError reports whether err is (or wraps) a LockUnavailableError.
func ResetWSLCacheForTests ¶
func ResetWSLCacheForTests()
ResetWSLCacheForTests resets the cached WSL detection so tests can control the outcome deterministically. Never call outside tests.
func WatchDisabledReason ¶
func WatchDisabledReason(projectRoot string, probe WatchProbe) string
WatchDisabledReason returns a human-readable reason why file watching should be skipped for projectRoot, or "" when watching should proceed.
Precedence (first match wins):
- CODEGRAPH_NO_WATCH=1 → off (explicit opt-out always wins)
- CODEGRAPH_FORCE_WATCH=1 → on (overrides auto-detection)
- WSL2 + /mnt/* drive → off (recursive fs.watch is too slow; #199)
Types ¶
type DirtyState ¶ added in v0.7.0
type DirtyState struct {
AcceptedGeneration uint64
CompletedGeneration uint64
WholeTree bool
Paths []PendingFile
}
DirtyState is a generation-safe snapshot of unresolved watcher hints. WholeTree is mutually exclusive with Paths and keeps storm/rename state bounded while preserving the accepted generation through retries.
type FileWatcher ¶
type FileWatcher struct {
// contains filtered or unexported fields
}
FileWatcher watches a project root for source-file changes and calls a debounced sync callback.
func New ¶
func New(root string, syncFn SyncFunc, opts Options) *FileWatcher
New creates a FileWatcher that has not yet started. Call FileWatcher.Start to begin watching.
func NewWithPaths ¶ added in v0.6.0
func NewWithPaths(root string, syncFn SyncPathsFunc, opts Options) *FileWatcher
NewWithPaths creates a FileWatcher whose reconciliation callback receives the exact dirty paths in each batch. Call FileWatcher.Start to begin watching.
func (*FileWatcher) DirtyState ¶ added in v0.7.0
func (fw *FileWatcher) DirtyState() DirtyState
DirtyState returns unresolved path or whole-worktree hints together with the accepted and successfully completed generations.
func (*FileWatcher) Drain ¶ added in v0.7.0
func (fw *FileWatcher) Drain(ctx context.Context) error
Drain captures the current accepted event generation and waits until a successful reconciliation has absorbed at least that generation. Events accepted after the capture belong to normal subsequent work and do not move this barrier.
func (*FileWatcher) FatalErrors ¶ added in v0.6.0
func (fw *FileWatcher) FatalErrors() <-chan error
FatalErrors reports native watcher failures that make complete coverage impossible. The caller should stop the watcher and surface the error.
func (*FileWatcher) IngestEventForTests ¶
func (fw *FileWatcher) IngestEventForTests(relPath string)
IngestEventForTests feeds a synthetic project-relative path through the full filter → pendingFiles → debounce pipeline. Only for use in tests.
func (*FileWatcher) IsActive ¶
func (fw *FileWatcher) IsActive() bool
IsActive reports whether the watcher is currently running.
func (*FileWatcher) PendingFiles ¶
func (fw *FileWatcher) PendingFiles() []PendingFile
PendingFiles returns a snapshot of files seen since the last successful sync.
func (*FileWatcher) Start ¶
func (fw *FileWatcher) Start() bool
Start begins watching. Returns true if watching started, false if disabled (e.g., CODEGRAPH_NO_WATCH or WSL2 /mnt drive).
func (*FileWatcher) StartWithError ¶ added in v0.6.0
func (fw *FileWatcher) StartWithError() error
StartWithError begins watching and returns the reason native watching could not be established. Start is retained as the compatibility bool API.
func (*FileWatcher) Stop ¶
func (fw *FileWatcher) Stop()
Stop signals the watcher to stop and returns without joining callbacks or in-flight reconciliation. It is safe to call from observation and sync callbacks. Use StopAndWait before closing callback-owned resources.
func (*FileWatcher) StopAndWait ¶ added in v0.6.0
func (fw *FileWatcher) StopAndWait()
StopAndWait shuts down the watcher and waits for final observations and callbacks. Callers should not invoke it from inside an observation or sync callback; callback code can safely use Stop instead.
func (*FileWatcher) WaitUntilReady ¶
func (fw *FileWatcher) WaitUntilReady(timeout time.Duration) error
WaitUntilReady blocks until the watch set is established, or until the context deadline is reached.
type IsIgnoredFunc ¶
IsIgnoredFunc reports whether a project-relative POSIX path should be ignored entirely (not just non-source, but also not a directory to recurse into on Linux).
type IsSourceFileFunc ¶
IsSourceFileFunc reports whether a project-relative POSIX path is a source file that should be indexed.
type LockUnavailableError ¶
type LockUnavailableError struct {
// contains filtered or unexported fields
}
ErrLockUnavailable signals that the sync callback could not acquire the cross-process write lock. The watcher keeps pendingFiles intact and reschedules rather than reporting this as an error. Matches the original's LockUnavailableError.
func NewLockUnavailableError ¶
func NewLockUnavailableError(msg string) *LockUnavailableError
NewLockUnavailableError wraps a message as a LockUnavailableError.
func (*LockUnavailableError) Error ¶
func (e *LockUnavailableError) Error() string
type Observation ¶ added in v0.6.0
type Observation struct {
Kind ObservationKind
At time.Time
Path string
Operation string
FullReconcileReason string
OperationID uint64
EventsReceived uint64
DirtyPaths int
CoalescedEvents uint64
IgnoredEvents uint64
QueuedFor time.Duration
Debounce time.Duration
Duration time.Duration
TotalDuration time.Duration
NoOp bool
Result SyncResult
Err error
}
Observation is a structured, optional diagnostic emitted by FileWatcher. It lets CLI and daemon callers render their own logs without coupling this package to a terminal or logging framework.
type ObservationKind ¶ added in v0.6.0
type ObservationKind string
ObservationKind identifies one point in the watcher event or reconciliation lifecycle. Observations are diagnostics only and never drive graph updates.
const ( ObservationEventReceived ObservationKind = "event_received" ObservationOperationStarted ObservationKind = "operation_started" ObservationOperationCompleted ObservationKind = "operation_completed" ObservationOperationFailed ObservationKind = "operation_failed" ObservationOperationRetry ObservationKind = "operation_retry" ObservationWatcherError ObservationKind = "watcher_error" )
type Options ¶
type Options struct {
// DebounceMs is the debounce delay in ms. 0 uses DefaultDebounceMS.
// Override via CODEGRAPH_WATCH_DEBOUNCE_MS env var (read at construction).
DebounceMs int
// OnSyncComplete is called after each successful sync.
OnSyncComplete func(SyncResult)
// OnSyncError is called when syncFn returns an error that is NOT
// ErrLockUnavailable.
OnSyncError func(error)
// OnObservation receives structured event and reconciliation diagnostics.
// It must return promptly. The callback is never invoked while the watcher
// mutex is held.
OnObservation func(Observation)
// IsSourceFile decides whether a project-relative path should be tracked.
// Defaults to a built-in set of Go/TS/JS extensions.
IsSourceFile IsSourceFileFunc
// IsIgnored decides whether a project-relative path should be dropped
// entirely (before the IsSourceFile check). Defaults to nil (nothing extra
// ignored beyond .codegraph/ and .git/).
IsIgnored IsIgnoredFunc
// Now overrides the clock. Defaults to time.Now.
Now NowFunc
// MaxDirWatches caps the Linux per-directory watch count. 0 = DefaultMaxDirWatches.
MaxDirWatches int
// MaxBatchEvents is the accepted-event count after which path precision is
// replaced by one whole-worktree-dirty marker. 0 = DefaultMaxBatchEvents.
MaxBatchEvents int
// InertForTests disables all OS-level watchers. Events are only fed
// through [FileWatcher.IngestEventForTests].
InertForTests bool
}
Options configures a FileWatcher.
type PendingFile ¶
type PendingFile struct {
// Path is the project-relative POSIX path (e.g. "src/foo.ts").
Path string
// FirstSeenMs is the wall-clock ms at the first event since the last sync.
FirstSeenMs int64
// LastSeenMs is the wall-clock ms at the most-recent event.
LastSeenMs int64
// Indexing is true when a sync is in flight that started after this
// file's most-recent event — meaning the next successful sync will
// absorb the edit.
Indexing bool
}
PendingFile is a source file the watcher observed since the last successful sync. Exposed via FileWatcher.PendingFiles so callers can flag stale results without blocking on a sync.
type SyncFunc ¶
type SyncFunc func() (SyncResult, error)
SyncFunc is the callback the watcher invokes after each debounce window. It should return ErrLockUnavailable when the cross-process write lock is held; the watcher retries without clearing pendingFiles in that case.
type SyncPathsFunc ¶ added in v0.6.0
type SyncPathsFunc func(paths []string) (SyncResult, error)
SyncPathsFunc is the callback the watcher invokes with the exact dirty project-relative paths in the current debounce batch. A nil slice requests authoritative whole-worktree rebuilding after an ambiguous rename event.
type SyncResult ¶
type SyncResult struct {
FilesChanged int
FilesChecked int
FilesAdded int
FilesModified int
FilesRemoved int
NodesUpdated int
DurationMs int64
FullReindex bool
}
SyncResult is the value returned by a successful sync callback.
type WatchProbe ¶
type WatchProbe struct {
// Env overrides os.Environ lookups. nil means use os.Getenv.
Env map[string]string
// IsWSL overrides the WSL detection when non-nil.
IsWSL *bool
}
WatchProbe holds injectable inputs for WatchDisabledReason so tests can control the decision without touching real env vars or /proc/version.