Skip to content

Configuration

Fresh uses a layered configuration system that allows you to customize settings at different levels of scope.

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

When Fresh loads configuration, it merges all layers together. The merge behavior depends on the type of setting:

Simple Values (strings, numbers, booleans)

Higher layers completely 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

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

Using the Settings UI

The easiest way to configure Fresh is through the Settings UI:

  1. Open Settings: Press Ctrl+, or use Command Palette → "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
    }
  }
}

Process Resource Limits

To prevent LSP servers from consuming too many resources, Fresh can limit their memory and CPU usage. This is configured in the process_limits section of your config.json file.

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

Released under the Apache 2.0 License