Configuration

July 9, 2026 · View on GitHub

The extension supports the following configuration options. All settings are prefixed with sight. and can be configured in VS Code's Settings UI or in your settings.json file.

Contents

Diagnostics

Control how the LSP reports errors, warnings, and other diagnostics.

SettingTypeDefaultDescription
sight.diagnostics.enabledbooleantrueEnable or disable all diagnostics
sight.diagnostics.severity.undefinedMacroenum"warning"Severity level for undefined macro references. Options: "error", "warning", "information", "hint", "off"
sight.diagnostics.severity.undefinedVariableenum"off"[Experimental] Severity level for undefined variable references. Options: "error", "warning", "information", "hint", "off"
sight.diagnostics.severity.styleWarningsenum"information"Severity level for style warnings. Options: "error", "warning", "information", "hint", "off"
sight.diagnostics.severity.malformedOperatorenum"warning"Severity for malformed operator diagnostics (e.g., = = instead of ==). Options: "error", "warning", "information", "hint", "off"
sight.diagnostics.severity.spacedCompoundOperatorenum"information"Severity for spaced compound operator diagnostics that Stata accepts as the compact form (e.g., < =<=). Options: "error", "warning", "information", "hint", "off"
sight.diagnostics.severity.invalidOperatorSequenceenum"error"Severity for invalid operator sequence diagnostics (e.g., < |). Options: "error", "warning", "information", "hint", "off"
sight.diagnostics.severity.cStyleLogicalInControlFlowenum"information"Severity for C-style logical operators (&&, ||) in if/else if control flow statements. These work but are stylistically discouraged. Options: "error", "warning", "information", "hint", "off"
sight.diagnostics.severity.chainedComparisonenum"warning"Severity for suspicious chained comparisons (e.g., a != b != c). Stata evaluates comparisons left-to-right, not as a mathematical chain. Options: "error", "warning", "information", "hint", "off"
sight.diagnostics.severity.literalMacroAdjacencyenum"hint"Severity for a literal placed directly against a macro reference in an expression (e.g., a == 1\b'). Options: "error", "warning", "information", "hint", "off"`
sight.diagnostics.indentationbooleanfalseEnable indentation diagnostics (missing indentation in blocks, unnecessary indentation after comments)

When the scope resolver can prove a referenced macro or variable exists but is unreachable, Sight replaces the generic undefined-symbol diagnostic with a more specific OUT_OF_SCOPE_SYMBOL message at the same severity. This applies when the symbol is defined later in the same file, defined after the relevant call site in another file, or excluded because local macros do not inherit through do/run. If the underlying undefined diagnostic is disabled, the rewrite is also suppressed.

Why Indentation Diagnostics Are Disabled by Default

Unlike Python, Stata ignores indentation - it's purely stylistic and doesn't affect code execution. Indentation diagnostics are disabled by default for several reasons:

  1. Stylistic, not semantic: "Wrong" indentation won't break your code
  2. Legacy codebase noise: Existing codebases may produce many warnings, causing alert fatigue
  3. Subjective preferences: Teams may have different indentation conventions
  4. Opt-in philosophy: Mature LSPs (TypeScript, ESLint) default stylistic rules to off

To enable indentation diagnostics:

  • VS Code: Set sight.diagnostics.indentation to true in Settings
  • Project config: Add [diagnostics] indentation = true to sight.toml

Forward Reference Detection

The LSP detects "forward references" - using a macro before it's defined in execution order. Stata executes code sequentially, so a macro doesn't exist until the line defining it runs.

Local macros must be defined before use within the same file:

// Warning: `fruit' is not yet defined
local result: list fruit - other
local fruit apple banana
local other banana

Global macros in the same file must also be defined before use:

// Warning: file_global is not yet defined
local result: list file_global - other
global file_global value

When a foreach or forvalues loop creates macro names from statically known iterator values, Sight expands those concrete names and treats them as defined from the local or global statement that creates them. A reference later in the same loop body is in scope; a reference before that statement is still a forward reference.

foreach g in age sex educ {
    summarize `g'
    local mean_`g' = r(mean)   // defines mean_age, mean_sex, mean_educ
}
display `mean_age'             // no "undefined macro" warning

This covers the common name-building forms — local `i', local `i'_suffix, local prefix_`i' — across foreach VAR in <list>, foreach VAR of local <macro>, and forvalues VAR = <range> when the values are statically known. When a value is dynamic (e.g. forvalues i = 1/`=_N') the loop is left alone and no names are expanded.

Global macros from other files also produce warnings unless the LSP can determine the relationship. By default (crossFile.backwardDependencies: "auto"), the LSP scans the workspace at startup to discover which files call which, and automatically resolves parent–child symbol inheritance. It also follows do, run, and include commands within each file for forward resolution. For cases where auto-detection doesn't work (e.g., dynamic paths with macros), you can use explicit directives (sight: done-by, sight: included-by, sight: do, sight: run, sight: include). The older @lsp- forms remain permanent aliases. The workspace indexer provides globals for completions and go-to-definition, but does not suppress undefined macro warnings. See Cross-File Awareness for details.

First definition wins: When a macro is defined multiple times, references before the first definition produce warnings, but references after the first definition do not (even if they appear before later redefinitions).

Macro-creating options: The analyzer recognizes local() and global() options on built-in commands (levelsof, glevelsof) and user-defined programs (via c_local `option' and global `option' patterns matching syntax declarations).

Mata st_local / st_global: The analyzer recognizes the two-argument setter forms st_local("name", value) and st_global("NAME", value) inside Mata blocks — both the inline mata: form and mataend / mata { … } blocks — and declares the named macro. The name must be a literal double-quoted identifier; dynamic names (variables, expressions, compound quotes, or embedded macro references) are not resolved. The one-argument read form st_local("name") does not declare anything. The declaration is forward-only: a reference before the setter is still flagged. For an inline mata: setter the macro only becomes visible after the inline statement ends, so a `name' reference within the same mata: unit — including the setter's own value argument (mata: st_local("x", "x'")`) — is reported undefined, matching Stata's macro expansion order; references after the statement resolve normally.

Tuning autocomplete behavior

If autocomplete feels too aggressive, there are a few knobs to adjust.

  • When the completions menu appears. By default it pops up almost instantly as you type. You can delay it, or turn automatic popups off entirely and trigger the menu manually with Ctrl+Space.
  • How completions get accepted. By default both Enter and Tab accept the highlighted suggestion when the menu is open. You can turn off Enter (so Enter always inserts a newline), turn off Tab (so Tab always indents), or both.

Changing settings

The first three knobs below are editor.* settings. Open the VS Code Settings UI either with the keyboard (Cmd+, on macOS, Ctrl+, on Windows/Linux) or through the menu (Code → Settings → Settings on macOS, File → Preferences → Settings on Windows/Linux). Paste the setting ID into the search box at the top, then change the value inline.

SettingDefaultWhat it does / how to change
editor.acceptSuggestionOnEnter"on""on" accepts on Enter; "off" makes Enter always insert a newline; "smart" only accepts when the suggestion would change the code. Set to "off" if you want Enter to always start a new line, even with the menu open.
editor.quickSuggestionsDelay10 msRaise this (e.g., 500) to delay the popup so it doesn't appear mid-keystroke. Or set editor.quickSuggestions to "off" entirely and use Ctrl+Space to trigger suggestions on demand.
editor.acceptSuggestionOnCommitCharactertrueTyping a "commit character" like ., (, or : both accepts the highlighted suggestion and inserts the character. Set to false if you'd rather those characters insert literally and only explicit keys (Tab / Enter) accept.

Stopping Tab from accepting suggestions

VS Code doesn't ship an editor.acceptSuggestionOnTab toggle — unbinding Tab is a keyboard shortcut change rather than a setting. Open the Keyboard Shortcuts editor either with the keyboard (Cmd+K Cmd+S on macOS, Ctrl+K Ctrl+S on Windows/Linux) or through the menu (Code → Settings → Keyboard Shortcuts on macOS, File → Preferences → Keyboard Shortcuts on Windows/Linux). Search for acceptSelectedSuggestion — the entry bound to Tab is the one you want. Right-click it and choose Remove Keybinding. Tab will now indent even when the completions menu is open, while Enter still accepts (unless you also changed editor.acceptSuggestionOnEnter).

If you'd rather edit the JSON directly, open keybindings.json (from the Keyboard Shortcuts editor, click the "Open Keyboard Shortcuts (JSON)" icon in the top right) and add:

{
  "key": "tab",
  "command": "-acceptSelectedSuggestion",
  "when": "suggestWidgetVisible && textInputFocus && !editorReadonly"
}

The leading - removes Tab from that command specifically.

Indexing

Configure workspace indexing behavior for cross-file features.

SettingTypeDefaultDescription
sight.indexWorkspacebooleantrueEnable workspace-wide symbol indexing
sight.indexing.maxFileSizeBytesnumber500000Maximum file size in bytes for indexing (~500KB)

Excluding files and directories

Exclude generated or vendored directories from analysis with workspace-relative glob patterns. This is most useful for repositories that mix hand-written Stata source with generated output (for example, downloaded .do files under output/) that would otherwise dominate sight check results.

SettingTypeDefaultDescription
sight.excludearray[]Workspace-relative glob patterns to skip during bulk analysis

You can set this in two places:

  • sight.toml at the project root, as workspace.exclude. This is read by both the editor (LSP server) and the sight check CLI:

    [workspace]
    exclude = ["output/**", "**/_generated/**", "**/*.gen.do"]
    
  • VS Code settings — your User settings.json or the project's Workspace settings (.vscode/settings.json) — as the sight.exclude key. This affects the editor only:

    {
      "sight.exclude": ["output/**", "**/_generated/**", "**/*.gen.do"]
    }
    

sight check (the CLI used in CI) reads only sight.toml (or a file passed with --config). It does not read VS Code settings, which are client-side and reach only the running LSP server. So to exclude paths from CI runs, put them in sight.toml. When both are present for the editor, the LSP server lets sight.toml take precedence over VS Code settings (see Project Configuration File).

Behavior:

  • sight check does not read or report diagnostics for excluded paths when walking a directory (including the default workspace-root walk).
  • The workspace indexer skips excluded paths when building cross-file context.
  • Explicitly-named files are always checked. sight check output/foo.do analyzes that file even when output/** is excluded; exclusion only filters directory walks.
  • Files opened directly in the editor are still analyzed. Exclusion governs bulk scanning and sight check, not in-editor live diagnostics — if you open a generated file you still get diagnostics and navigation for it.

Patterns use picomatch glob syntax (*, **, ?, {a,b}) and are matched relative to the workspace root. A leading ! re-includes a previously-excluded path (e.g. ["output/**", "!output/manifest.do"]).

exclude only applies to files inside your workspace folder. Sight also scans your configured ADO paths (and auto-detected Stata install directories), which normally live outside the workspace — exclude has no effect on those files, so you cannot use it to filter ADO scanning. (If you point an ADO path at a folder inside the workspace, it is in-workspace like any other file and exclude patterns do apply to it.)

ADO Paths

Configure additional search paths for ADO files.

SettingTypeDefaultDescription
sight.adoPathsarray[]Additional paths to search for ADO files

Example:

{
  "sight.adoPaths": [
    "/path/to/custom/ado",
    "/another/ado/directory"
  ]
}

Auto-detected Stata install paths

In addition to the paths you configure, Sight automatically consults the standard Stata install and user ado directories when resolving help topics for the SMCL help viewer:

  • macOS: /Applications/Stata/ado/{base,site,updates}, its StataMP / StataSE / StataBE / StataIC variants, the StataNow channel directory /Applications/StataNow/ado/{base,site,updates}, and ~/Library/Application Support/Stata/ado/{personal,plus}.
  • Linux: /usr/local/stata<version>/ado/{base,site,updates} and /opt/stata<version>/… for versions 13–20, plus unversioned stata directories.
  • Windows: Stata<version>\ado\{base,site,updates} under each Program Files / Program Files (x86) directory that Windows reports via its environment variables.
  • All platforms: the legacy ~/ado, ~/ado/personal, and ~/ado/plus conventions.

Only directories that actually exist are used, and the auto-detected paths are only consulted for .sthlp lookups — they are not scanned into the workspace symbol index. Use sight.adoPaths to override or supplement this list when you install Stata in a non-standard location.

Comments

Configure the line comment character used by VS Code's toggle comment shortcut.

SettingTypeDefaultDescription
sight.lineCommentStylestring"//"Line comment character used by the toggle comment shortcut. In Stata, // can appear anywhere on a line while * must be at the start of a line. Options: "//", "*"

To use * for comment toggling instead of //:

{
    "sight.lineCommentStyle": "*"
}

Project Configuration File

Sight reads sight.toml as the portable project configuration file. The LSP discovers it by walking upward from the active workspace root; sight check will use the same discovery from --workspace.

Project config wins over client/editor settings per key. Keys omitted from sight.toml continue to come from editor settings or built-in defaults.

In a multi-root workspace, the project config is loaded only from the first workspace folder and applied to every document; a separate sight.toml in another folder is not resolved per-root. The server logs a note when it detects multiple folders. Open one project per window, or place a single sight.toml at a shared root, if you need distinct project settings.

.sight.json is no longer supported. Convert its contents to TOML syntax; renaming the file is not enough because JSON and TOML are different languages.

Naming convention and case aliasing

The canonical spelling for every setting is camelCase (e.g. crossFile.maxChainDepth, formatting.preserveAlignment, diagnostics.severity.undefinedMacro). This is the form used throughout this documentation and matches comparable tools (Pyright, rust-analyzer, typescript-language-server).

As a permanent guarantee, every setting also accepts its snake_case spelling as an equivalent alias, and vice-versa — so you never have to remember which form a given setting uses. crossFile.maxChainDepth and cross_file.max_chain_depth are identical, as are formatting.preserveAlignment and formatting.preserve_alignment. Section names alias too (crossFilecross_file), and matching is case-insensitive overall (CrossFile also works). You may even mix conventions within a single config.

This aliasing applies uniformly to every configuration entry point: sight.toml, editor settings.json (VS Code), and the initializationOptions non-VS-Code clients send (Neovim, Helix, Zed, Claude Code). When two spellings of the same key appear together and neither is the canonical camelCase form, both are ignored and a warning is emitted; when one of them is canonical, the canonical spelling wins.

indexWorkspace = true
adoPaths = []
lineCommentStyle = "//"
debug = false

[workspace]
exclude = ["output/**"]

[diagnostics]
enabled = true
indentation = false

[diagnostics.severity]
undefinedMacro = "warning"
undefinedVariable = "off"
styleWarnings = "information"
malformedOperator = "warning"
spacedCompoundOperator = "information"
invalidOperatorSequence = "error"
cStyleLogicalInControlFlow = "information"
mixedLogicalOperators = "warning"

[formatting]
indentSize = 4
indentStyle = "spaces"
lineWidth = 80
preferredCommentStyle = "line"
normalizeCommentStyle = false
commentLineWidth = 72
mode = "source-preserving"
preserveAlignment = true

[completion]
cacheSize = 200
prefixMaxItems = 200

[indexing]
maxFileSizeBytes = 500000

[crossFile]
indexWorkspace = true
maxIndexedFiles = 1000
assumeCallSite = "end"
backwardDependencies = "auto"
maxBackwardDepth = 10
maxForwardDepth = 10
maxChainDepth = 20
maxCalleeRevalidations = 10
maxCachedFiles = 2000
maxCachedScopes = 1000
maxCachedForwardClosures = 2000

[crossFile.diagnostics]
missingFile = "warning"
maxDepth = "information"
callSiteIdentification = "information"
caseMismatch = "auto"
OptionTypeDefaultDescription
indexWorkspacebooleantrueGlobal workspace-indexing switch
adoPathsstring array[]Additional ADO search paths
workspace.excludestring array[]Workspace-relative globs to skip during sight check and indexing
lineCommentStyle"//" | "*""//"Line comment style used when formatting resolves "line"
debugbooleanfalseEnable debug logging
diagnostics.enabledbooleantrueEnable diagnostics
diagnostics.indentationbooleanfalseEnable indentation diagnostics
diagnostics.severity.*severityvariesSeverity for diagnostic families
formatting.*mixedvariesServer formatting behavior
completion.cacheSizenumber200Completion cache size
completion.prefixMaxItemsnumber200Maximum prefix-command completions
indexing.maxFileSizeBytesnumber500000Maximum indexed file size
crossFile.backwardDependencies"auto" | "explicit""auto""auto": discover parents from workspace scan; "explicit": require directives
crossFile.indexWorkspacebooleantrueEnable workspace-wide file indexing
crossFile.maxIndexedFilesnumber1000Maximum files to index
crossFile.maxBackwardDepthnumber10Maximum recursion depth for backward directive resolution
crossFile.maxForwardDepthnumber10Maximum recursion depth for forward scope resolution
crossFile.maxChainDepthnumber20Maximum combined depth for forward + backward resolution
crossFile.maxCalleeRevalidationsnumber10Maximum number of open callee documents to revalidate per caller change
crossFile.maxCachedFilesnumber2000LRU capacity of the parsed-file cache. A memory-safety backstop: eviction only costs recomputation, never correctness. Very low values degrade performance on deep chains.
crossFile.maxCachedScopesnumber1000LRU capacity of the resolved-scope cache
crossFile.maxCachedForwardClosuresnumber2000LRU capacity of the forward-closure memo (see docs/cross-file.md)
crossFile.assumeCallSite"end" | "start""end"Where to assume call site when not specified and inference fails
crossFile.diagnostics.missingFileseverity"warning"Severity for missing directive file diagnostics
crossFile.diagnostics.callSiteIdentificationseverity"information"Severity for call site identification diagnostics
crossFile.diagnostics.caseMismatchseverity + "auto""auto"Severity for case-only path mismatch diagnostics. "auto" maps to information on case-insensitive filesystems (macOS/Windows) and warning on case-sensitive ones (Linux/CI). Independent of missingFile; not suppressible via sight: ignore — silence with "off" or fix the path.

Severity options: "error", "warning", "information", "off" (alias: "info" for "information"). crossFile.diagnostics.caseMismatch also accepts "auto" (not valid for other cross-file severity keys).

Both indexWorkspace and crossFile.indexWorkspace default to true. Workspace indexing runs only when both are enabled; setting either to false disables it.

The shared canonical paths and permanent compatibility aliases are specified in Shared project configuration schema.

See Also

Additional settings are documented alongside their features: