14.5. Variables
Prev | Chapter 14. Lua Debugger | Next

## 14.5. Variables

The Variables collapsible section shows three roots:

- Locals — parameters and local variables for the currently selected stack frame.
- Upvalues — values the function actually references from its enclosing scope. Functions that do not close over anything have no upvalues.
- Globals — names from Lua’s global environment.

`Globals` resolves against `_G` first and then against the paused frame’s `_ENV` upvalue. The `_ENV` fallback matters for scripts loaded with a file environment (for example via `dofile`) — a top-level `my_proto = Proto.new(…​)` in such a script lands in the file’s `_ENV` rather than `_G`, and the debugger still lists it under Globals.

The tree is populated lazily: a row is only inspected when you expand it. Sub-trees stop at the same path-depth cap as Watch (see Section 14.6.2, “Path-watch syntax”).

The tree has two columns, Name and Value; the underlying type is folded into the row tooltip rather than shown in its own column. For userdata values (Wireshark class instances such as `Pinfo`, `Tvb`, `ProtoField`), the tooltip reads `userdata (ClassName)` when the instance metatable exposes a `__name`, otherwise just `userdata`.

Functions are listed alongside other values (rendered as `function: 0xADDR`, the standard Lua tostring shape) so callbacks, locally-bound helpers, methods on userdata, and stdlib namespaces like `string` and `table` all show up in their respective scopes. Function rows are not expandable; to inspect what a function does, view its source via Stack Trace once it is on the call stack.

Selecting a different frame in Stack Trace rebuilds the Variables tree for that frame.

A context menu on any row offers Copy Name, Copy Value (the full, untruncated value — the tree itself truncates long values for display), Copy Name & Value (`name = value`), Copy Path (the canonical Variables-tree path, ready to paste into the Watch panel or the Evaluate panel), and Add Watch (prefilled with the row’s variable path; the menu label includes the path so you can see exactly what would be added).

### 14.5.1. Changed-value cue

Both the Variables tree and the Watch tree highlight values that changed since the previous pause: the row is drawn in a bold accent foreground from the current theme and briefly flashes a softer highlight tint. New rows (a local that did not exist last pause, a key added to a table, a freshly appearing Watch child) receive the same treatment. Watch roots deliberately do not flash on first sighting — a new root is usually a spec you just typed.

The cue is anchored to the stack frame that was active when the pause started, because "the value changed" only makes sense against a comparable earlier reading:

- Selecting a different frame in the Stack Trace mid-pause shows an unrelated scope, so the cue is suppressed on Locals, Upvalues, and any unqualified or Locals.* / Upvalues.* watch rows while you are in a non-entry frame. Globals.* rows stay comparable across frame switches and keep their highlight.
- A pause that landed inside a different function than the previous pause also suppresses the cue on Locals and Upvalues, to avoid spuriously lighting up "every local is new" after stepping across a call or return. Pauses that hit the same function again — for example the next iteration of a loop, or a recurring dissector callback — still show the cue.

The change baseline is cleared, so the next pause shows no cue, after editing a watch’s spec, removing watches, reloading Lua plugins, or disabling the debugger. A failed watch lookup invalidates that one watch’s baseline only.

Empty strings count as real values for the comparison: a variable whose value was `""` last pause and is still `""` now does not flash. Single-stepping never flickers the Watch Value column through its not-paused em-dash placeholder, even though a step technically resumes and re-pauses; the tree repaints straight from the new pause’s values.

Prev | Up | Next
14.4. Toolbar | Home | 14.6. Watch