
# tecs.io.watcher


Polls loaded content and dispatches changes to reload handlers.

```teal
local watcher <const> = tecs.io.watcher

if watcher.isSupported() then
    watcher.on(
        "document",
        function(change: watcher.Change)
            print("reload " .. change.path)
        end
    )
    watcher.install({intervalSeconds = 0.25})
end
```

An [`Application`](/modules/Application/) with `watch` in its configuration
installs the watcher and calls `tecs.runtime.poll` once per host iteration,
including while the game is suspended or handling a crash. Game systems,
plugins, and update functions do not poll. A headless program without an
application owns the manual loop shown on `poll`.

The watcher scans only paths recorded by `tecs.io.files.read` and the asset
loaders. It waits for a changed file's
size and modification time to settle before dispatching it. Empty, missing,
and directory paths do not dispatch.

Handlers run between frames. A handler error reaches the log and does not
replace the current resource. Release builds report `isSupported() == false`
and do not poll.

## Module contents

### Types

| Type | Kind | Description |
| --- | --- | --- |
| [`Change`](/modules/io/watcher/#tecs.io.watcher.Change) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Change describes one settled content change. |
| [`ChangeHandler`](/modules/io/watcher/#tecs.io.watcher.ChangeHandler) | <span class="tealdoc-kind-badge tealdoc-kind-type">type</span> | ChangeHandler receives one settled change. |
| [`Config`](/modules/io/watcher/#tecs.io.watcher.Config) | <span class="tealdoc-kind-badge tealdoc-kind-record">record</span> | Config controls polling frequency, settling and path scope. |

### Functions

| Function | Kind | Description |
| --- | --- | --- |
| [`dispatched`](/modules/io/watcher/#tecs.io.watcher.dispatched) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns the changes dispatched since install. |
| [`install`](/modules/io/watcher/#tecs.io.watcher.install) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Starts watching loaded content. |
| [`isInstalled`](/modules/io/watcher/#tecs.io.watcher.isInstalled) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns whether the watcher is running. |
| [`isSupported`](/modules/io/watcher/#tecs.io.watcher.isSupported) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns whether this build supports hot reload. |
| [`kinds`](/modules/io/watcher/#tecs.io.watcher.kinds) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns registered content kinds in sorted order. |
| [`on`](/modules/io/watcher/#tecs.io.watcher.on) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Registers the handler for a content kind. |
| [`poll`](/modules/io/watcher/#tecs.io.watcher.poll) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Advances a manually driven headless watcher. |
| [`scan`](/modules/io/watcher/#tecs.io.watcher.scan) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Looks at every watched path once, whatever the interval says. |
| [`uninstall`](/modules/io/watcher/#tecs.io.watcher.uninstall) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Stops watching and forgets every path's state. |
| [`unsettled`](/modules/io/watcher/#tecs.io.watcher.unsettled) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns unsettled changed paths in sorted order. |
| [`watching`](/modules/io/watcher/#tecs.io.watcher.watching) | <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span> | Returns watched paths in sorted order. |

## Types

<a id="tecs.io.watcher.Change"></a>
### tecs.io.watcher.Change <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Change` describes one settled content change.


```teal
record tecs.io.watcher.Change
    path: string
    kind: string
end
```

<a id="tecs.io.watcher.Change.path"></a>
#### tecs.io.watcher.Change.path <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. The watcher sets `path` to the changed loaded path before
it calls the handler.


```teal
tecs.io.watcher.Change.path: string
```

<a id="tecs.io.watcher.Change.kind"></a>
#### tecs.io.watcher.Change.kind <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Read-only. The watcher sets `kind` to the content kind recorded by
the original load. Built-in loaders use `"image"`, `"sound"`,
`"font"`, `"shader"` or `"document"`; a custom loader supplies its
own kind to `tecs.io.files.read`.


```teal
tecs.io.watcher.Change.kind: string
```

<a id="tecs.io.watcher.ChangeHandler"></a>
### tecs.io.watcher.ChangeHandler <span class="tealdoc-kind-badge tealdoc-kind-type">type</span>

`ChangeHandler` receives one settled change.


```teal
type tecs.io.watcher.ChangeHandler = function(Change)
```

<a id="tecs.io.watcher.Config"></a>
### tecs.io.watcher.Config <span class="tealdoc-kind-badge tealdoc-kind-record">record</span>

`Config` controls polling frequency, settling and path scope.


```teal
record tecs.io.watcher.Config
    intervalSeconds: number
    settle: integer
    root: string
end
```

<a id="tecs.io.watcher.Config.intervalSeconds"></a>
#### tecs.io.watcher.Config.intervalSeconds <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Sets the delay between automatic polls in seconds.
It defaults to 0.5. Direct calls to `scan` ignore this delay.


```teal
tecs.io.watcher.Config.intervalSeconds: number
```

<a id="tecs.io.watcher.Config.settle"></a>
#### tecs.io.watcher.Config.settle <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Requires this many additional matching scans after
a change is first observed. It defaults to 1, so one scan observes
the change and the next confirms it. Zero dispatches immediately.


```teal
tecs.io.watcher.Config.settle: integer
```

<a id="tecs.io.watcher.Config.root"></a>
#### tecs.io.watcher.Config.root <span class="tealdoc-kind-badge tealdoc-kind-field">field</span>

Caller-writable. Limits watched paths to this root. It defaults to
the content root.


```teal
tecs.io.watcher.Config.root: string
```

## Functions

<a id="tecs.io.watcher.dispatched"></a>
### tecs.io.watcher.dispatched <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns the changes dispatched since `install`.



```teal
function tecs.io.watcher.dispatched(): integer
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `integer` | Returns the cumulative handler count. |

<a id="tecs.io.watcher.install"></a>
### tecs.io.watcher.install <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Starts watching loaded content.

The watcher records the current state of loaded paths and dispatches only
later changes. It raises when the build lacks hot-reload support.



```teal
function tecs.io.watcher.install(config: watcher.Config)
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `config` | [`watcher.Config`](/modules/io/watcher/#tecs.io.watcher.Config) | The caller supplies polling options or omits them for the documented defaults. |

#### Returns

None.

<a id="tecs.io.watcher.isInstalled"></a>
### tecs.io.watcher.isInstalled <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns whether the watcher is running.



```teal
function tecs.io.watcher.isInstalled(): boolean
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns true after `install` and before `uninstall`. |

<a id="tecs.io.watcher.isSupported"></a>
### tecs.io.watcher.isSupported <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns whether this build supports hot reload.



```teal
function tecs.io.watcher.isSupported(): boolean
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `boolean` | Returns true when this build can install the watcher. |

<a id="tecs.io.watcher.kinds"></a>
### tecs.io.watcher.kinds <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns registered content kinds in sorted order.



```teal
function tecs.io.watcher.kinds(): {string}
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `{string}` | Returns a fresh caller-owned list. |

<a id="tecs.io.watcher.on"></a>
### tecs.io.watcher.on <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Registers the handler for a content kind.

Re-registering a kind replaces its handler. Nil removes it.



```teal
function tecs.io.watcher.on(
    kind: string, handler: watcher.ChangeHandler
)
```

#### Arguments

| Name | Type | Description |
| --- | --- | --- |
| `kind` | `string` | The caller supplies the content kind to register. |
| `handler` | [`watcher.ChangeHandler`](/modules/io/watcher/#tecs.io.watcher.ChangeHandler) | The caller supplies the replacement handler or nil to remove the current one. |

#### Returns

None.

<a id="tecs.io.watcher.poll"></a>
### tecs.io.watcher.poll <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Advances a manually driven headless watcher.

[`runtime.poll`](/modules/runtime/#tecs.runtime.poll) calls this while the
watcher is installed. Game systems, plugins, and update functions must not
call it. A second call in the same loop can distort `intervalSeconds` and
the number of matching observations counted by `Config.settle`.

A headless program normally calls `tecs.runtime.poll` once per loop after
`install`. Call this narrower operation when a test or tool deliberately
drives only the watcher. It returns immediately until `intervalSeconds`
has elapsed, then scans only content already recorded by file or asset
loading. It performs no sleeping.



```teal
function tecs.io.watcher.poll(): integer
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `integer` | Returns how many handlers ran during this call. |

#### Examples

```teal
local watcher <const> = tecs.io.watcher

local function runHeadlessWatcher(keepRunning: function(): boolean)
    watcher.install({intervalSeconds = 0.25})
    while keepRunning() do
        watcher.poll()
    end
    watcher.uninstall()
end
```

<a id="tecs.io.watcher.scan"></a>
### tecs.io.watcher.scan <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Looks at every watched path once, whatever the interval says.

Call this directly when a headless tool or test controls poll timing.



```teal
function tecs.io.watcher.scan(): integer
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `integer` | Returns how many handlers ran, or zero when no watcher runs. |

<a id="tecs.io.watcher.uninstall"></a>
### tecs.io.watcher.uninstall <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Stops watching and forgets every path's state.

Registered handlers remain available for a later installation.


```teal
function tecs.io.watcher.uninstall()
```

#### Arguments

None.

#### Returns

None.

<a id="tecs.io.watcher.unsettled"></a>
### tecs.io.watcher.unsettled <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns unsettled changed paths in sorted order.



```teal
function tecs.io.watcher.unsettled(): {string}
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `{string}` | Returns a fresh caller-owned list. |

<a id="tecs.io.watcher.watching"></a>
### tecs.io.watcher.watching <span class="tealdoc-kind-badge tealdoc-kind-static">Static</span>

Returns watched paths in sorted order.



```teal
function tecs.io.watcher.watching(): {string}
```

#### Arguments

None.

#### Returns

| Type | Description |
| --- | --- |
| `{string}` | Returns a fresh caller-owned list. |