Skip to content

Configuration

  • Overview - Layered configuration system
  • Keyboard - Keyboard shortcuts and keybinding configuration

Fresh uses layered configuration.

Configuration Layers

Settings are loaded from multiple layers, with higher layers overriding lower ones:

LayerLocationScopeUse Case
SystemBuilt-in defaultsGlobalFactory defaults (read-only)
User~/.config/fresh/config.jsonAll projectsPersonal preferences
Project.fresh/config.json in project rootSingle projectProject-specific settings
Session.fresh/session.json (temporary)Current sessionTemporary overrides

Path Notes:

  • On Windows, User config is at %APPDATA%\fresh\config.json
  • Project config is found by searching up from the current directory for .fresh/config.json

How Layers Are Merged

Fresh merges all layers. Merge behavior depends on the setting type:

Simple Values (strings, numbers, booleans)

Higher layers override lower layers. If a setting is not specified in a higher layer, it falls through to the next lower layer.

System: theme = "default"    ← Base default
User:   theme = "dark"       ← Overrides system
Project: (not set)           ← Falls through
Session: theme = "light"     ← Final value: "light"

Nested Objects (editor, terminal, file_explorer)

Nested objects are deep-merged field by field. Each field follows the same "higher wins" rule independently.

Example: If User sets editor.tab_size = 4 and Project sets editor.line_wrap = true:

json
// User config
{ "editor": { "tab_size": 4, "line_numbers": true } }

// Project config
{ "editor": { "line_wrap": true } }

// Result: All fields merged
{ "editor": { "tab_size": 4, "line_numbers": true, "line_wrap": true } }

Languages Map (deep merge)

The languages map uses deep merging with field-level override:

  • Entries from all layers are combined (you can add new languages at any layer)
  • For the same language key, individual fields are merged (not replaced entirely)

Example: Extending built-in Rust settings in your project:

json
// System (built-in): rust has extensions, grammar, etc.
// Project config - only need to specify what you're changing:
{
  "languages": {
    "rust": {
      "tab_size": 2,
      "format_on_save": true
    }
  }
}
// Result: Rust keeps all system defaults, with tab_size and format_on_save overridden

LSP Map (deep merge)

The lsp map uses deep merging with field-level override:

  • Entries from all layers are combined
  • For the same language key, individual fields are merged (not replaced entirely)
  • Unspecified fields inherit from lower layers (you only need to specify what you're changing)

Example: To disable an LSP while preserving its default command:

json
{
  "lsp": {
    "rust": {
      "enabled": false
    }
  }
}
// Result: rust-analyzer command preserved from defaults, just disabled

Example: To add initialization options without repeating the command:

json
{
  "lsp": {
    "rust": {
      "initialization_options": { "checkOnSave": { "command": "clippy" } }
    }
  }
}
// Result: command="rust-analyzer" (from defaults) + your initialization_options

Lists (keybindings, on_save actions)

Lists are replaced entirely by higher layers - they are not merged or appended.

Example: If you define keybindings in your Project config, it completely replaces User keybindings (not extends them).

Removing/Unsetting Values

You cannot remove or unset a value from a lower layer — only override it. For boolean settings, you can set them to false to disable a feature enabled in a lower layer.

Using the Settings UI

To configure Fresh through the Settings UI:

  1. Open Settings: Use Edit → Settings... or Command Palette (Ctrl+P) → "Open Settings"
  2. Browse Categories: Use arrow keys or click to navigate
  3. Change Values: Toggle booleans, adjust numbers, select from dropdowns
  4. Choose Target Layer: Click the layer button (e.g., [ User ]) to switch between User/Project/Session
  5. Save: Press Enter on the Save button or use Ctrl+S

Advanced: Edit Config File Directly

For complex configurations (like LSP args or custom keybindings), click the [ Edit ] button in the Settings footer to open the raw JSON config file for the selected layer.

Example Configurations

User config (~/.config/fresh/config.json) - your personal defaults:

json
{
  "version": 1,
  "theme": "dark",
  "editor": {
    "tab_size": 4,
    "line_numbers": true
  }
}

Project config (.fresh/config.json) - project-specific overrides:

json
{
  "version": 1,
  "editor": {
    "tab_size": 2
  },
  "languages": {
    "javascript": {
      "formatter": "prettier --write"
    }
  }
}

Common Configuration Tasks

Add a Custom Language

To add syntax highlighting and LSP support for a new language:

json
{
  "languages": {
    "mylang": {
      "extensions": ["ml", "myl"],
      "grammar": "mylang",
      "comment_prefix": "#",
      "auto_indent": true
    }
  },
  "lsp": {
    "mylang": {
      "command": "mylang-lsp",
      "args": ["--stdio"],
      "enabled": true
    }
  }
}

Customize LSP Settings

Configure initialization options for a language server:

json
{
  "lsp": {
    "rust": {
      "command": "rust-analyzer",
      "enabled": true,
      "initialization_options": {
        "checkOnSave": { "command": "clippy" }
      }
    }
  }
}

Project-Specific Tab Size

Create .fresh/config.json in your project:

json
{
  "version": 1,
  "editor": {
    "tab_size": 2
  }
}

Layer Source Indicators

In the Settings UI, each setting shows where its current value comes from:

  • (user) - Set in your User config
  • (project) - Set in the Project config
  • (session) - Temporary session override
  • (no indicator) - Using system default

Editor Settings Reference

All settings can be changed via the Settings UI (command palette → "Open Settings").

Display

SettingDescriptionDefault
Line numbersShow line numbers in gutteron
Line wrapSoft-wrap long linesoff
RulersColumn positions for vertical ruler linesnone
Vertical scrollbarShow vertical scrollbaron
Horizontal scrollbarShow horizontal scrollbaroff
Terminal backgroundLet terminal background show throughoff
Bracket matchingHighlight matching bracket pairson
Status barShow/hide the status baron
Whitespace indicatorsShow space/tab characters (leading, inner, trailing)off
Diagnostics inline textShow diagnostics at end of lineoff

Editing

SettingDescriptionDefault
Tab sizeSpaces per indent level4
Auto-closeAuto-close brackets and quoteson
Auto-surroundWrap selection when typing a delimiteron
Trim trailing whitespace on saveRemove trailing whitespace when savingoff
Ensure final newline on saveAdd trailing newline when savingoff

Auto-Save

SettingDescriptionDefault
Auto-saveSave modified buffers to disk automaticallyoff
Auto-save intervalSeconds between auto-saves (when enabled)30
Recovery save intervalSeconds between crash-recovery saves2

Clipboard

SettingDescriptionDefault
OSC 52Use OSC 52 escape sequence for clipboardon
System clipboardUse system clipboardon

If copy/paste hangs (common with PuTTY), try disabling one or both of these.

Process Resource Limits

To prevent LSP servers from consuming too many resources, Fresh can limit their memory and CPU usage.

json
{
  "lsp": {
    "rust": {
      "command": "rust-analyzer",
      "enabled": true,
      "process_limits": {
        "max_memory_mb": 4096,
        "max_cpu_percent": 200
      }
    }
  }
}

See docs/PROCESS_LIMITS.md.

Released under the Apache 2.0 License