watch

package
v0.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 13, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

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

View Source
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).

View Source
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.

View Source
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

func EmitEventForTests(root, relPath string) bool

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

func IsLockUnavailableError(err error) bool

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):

  1. CODEGRAPH_NO_WATCH=1 → off (explicit opt-out always wins)
  2. CODEGRAPH_FORCE_WATCH=1 → on (overrides auto-detection)
  3. 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

type IsIgnoredFunc func(relPath string) bool

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

type IsSourceFileFunc func(relPath string) bool

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 NowFunc

type NowFunc func() time.Time

NowFunc returns the current wall-clock time. Injectable for tests.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL