How to Configure Language Server Protocol (LSP) for Various Programming Languages in Flow Control

Flow Control automatically detects file types and launches the appropriate language server based on mappings defined in src/file_type_lsp.zig, while allowing users to override commands via JSON configuration files.

To configure Language Server Protocol (LSP) for various programming languages in Flow Control, you work with the neurocyte/flow repository's built-in file type definitions and optional JSON override files. Flow Control handles LSP integration natively without external plugins, detecting file types automatically and spawning language servers according to predefined commands that you can customize globally or per-project.

Understanding Flow Control's Built-In LSP Integration

Flow Control implements LSP support directly in its Zig codebase, eliminating the need for third-party extensions. When you open a file, Flow Control executes a four-step process:

  1. File type detection — The editor identifies the language (e.g., python, rust, typescript) using internal file type tables.
  2. Command lookup — It retrieves the default LSP command from the language_server field in src/file_type_lsp.zig.
  3. Process management — The system calls Project.get_or_start_language_server to either reuse an existing server or launch a new process via LSP.open.
  4. Initialization — Flow Control sends the initialize request through Project.send_lsp_init_request, then forwards all subsequent LSP notifications and requests.

This architecture ensures that language servers start automatically when you open supported files, with each project maintaining its own server instances to prevent cross-contamination between codebases.

Default LSP Configuration in file_type_lsp.zig

The heart of Flow Control's LSP mapping resides in src/file_type_lsp.zig, where each language is defined as a public constant containing a language_server tuple. For example, the Python definition at line 179 specifies:

pub const python = .{
    .language_server = .{ "pylsp" },
};

The structure follows a consistent pattern across all supported languages:

pub const <language_id> = .{
    .language_server = .{ "<executable>", "<arg1>", "<arg2>", ... },
};

When Flow Control detects a file matching the <language_id>, it extracts the executable name and arguments from this tuple to construct the launch command. The defaults cover popular languages like Rust (rust-analyzer), TypeScript (typescript-language-server), and Go (gopls), but you can extend these definitions or override them without modifying the source code.

Customizing LSP Commands with JSON Overrides

While the built-in defaults work for most users, production environments often require custom flags, specific binary paths, or environment-specific arguments. Flow Control supports JSON override files that supersede the default commands without requiring source code changes.

Override File Locations

Flow Control searches for configuration files in two scopes, as implemented in src/lsp_config.zig:

  • Global overrides: $HOME/.config/flow/lsp/<lsp_name>.json
  • Project-local overrides: <project_root>/.flow/lsp/<lsp_name>.json

The helper function get_config_file_path (lines 32-68) constructs these paths dynamically based on the project context and configuration scope.

How Overrides Work

When you open a file in Flow Control, the UI entry point in src/tui/mainview.zig (lines 722-734) retrieves LSP options:

const language_server = file_type.language_server orelse return no_lsp_error();
const lsp_name = language_server[0];
const language_server_options = lsp_config.get(project, lsp_name) orelse &.{};

The lsp_config.get function reads the JSON file if present and returns its contents as a raw byte slice. This slice is passed directly to the LSP process as additional arguments, as seen in src/project_manager.zig at line 215.

Practical Override Examples

Global Python configuration with logging:

mkdir -p ~/.config/flow/lsp
cat > ~/.config/flow/lsp/python.json <<'EOF'
["pylsp", "--log-file", "/tmp/pylsp.log"]
EOF

Project-specific TypeScript configuration:

cd my-project
mkdir -p .flow/lsp
cat > .flow/lsp/typescript.json <<'EOF'
["typescript-language-server", "--stdio", "--tsserver-log-file", ".tsserver.log"]
EOF

These configurations take effect immediately when you open a file of the corresponding type, with project-local settings taking precedence over global ones.

Adding Support for New Languages

If you work with a language not included in Flow Control's default table, you can extend support by modifying src/file_type_lsp.zig. Each language entry follows a simple structure:

pub const <language_id> = .{
    .language_server = .{ "<executable>", "<arg1>", "<arg2>", ... },
};

For example, to add Dart support:

pub const dart = .{
    .language_server = .{ "dart_language_server", "--protocol=lsp" },
};

After adding the constant anywhere in the file (following the existing entries), rebuild Flow Control:

zig build

Verify the new language appears in the supported list:

flow -l | grep dart

Once built, opening any .dart file automatically launches your specified language server. You can still provide custom arguments via .flow/lsp/dart.json or ~/.config/flow/lsp/dart.json using the override mechanism described earlier.

Forcing a Specific Language Mode

Flow Control typically detects languages by file extension, but you can override this behavior using the -l or --language flag. This is useful when working with files that lack standard extensions or when you need to test a specific LSP configuration.

flow edit -l python myscript.txt

This command forces Flow Control to treat myscript.txt as Python, applying the Python LSP configuration (including any JSON overrides) regardless of the file extension.

Summary

  • Flow Control provides native LSP integration through the neurocyte/flow repository, automatically launching language servers based on file type definitions in src/file_type_lsp.zig.
  • Default commands are stored as Zig constants with language_server tuples containing executables and arguments.
  • JSON override files in ~/.config/flow/lsp/ (global) or .flow/lsp/ (project-local) allow customization of LSP arguments without recompiling.
  • New languages are supported by adding entries to src/file_type_lsp.zig and rebuilding with zig build.
  • Manual language selection is available via the -l flag to force specific LSP modes regardless of file extension.

Frequently Asked Questions

How does Flow Control determine which language server to start?

Flow Control determines the language server by first detecting the file type from the file extension or content, then looking up the corresponding entry in src/file_type_lsp.zig. Each entry contains a language_server field specifying the executable name and default arguments. If an override JSON file exists in ~/.config/flow/lsp/ or .flow/lsp/, Flow Control merges those arguments with the defaults before launching the process via Project.get_or_start_language_server.

Can I use different language server settings for different projects?

Yes. Flow Control supports project-local LSP configuration through the .flow/lsp/ directory. When you open a file, the editor checks for JSON files in the current project's .flow/lsp/ folder before falling back to the global ~/.config/flow/lsp/ directory. This allows you to specify project-specific flags, logging paths, or even different language server binaries for individual repositories without affecting your global settings.

What should I do if my programming language isn't supported by default?

If your language isn't defined in src/file_type_lsp.zig, you can add support by editing that file and adding a new public constant following the existing pattern: pub const <language_id> = .{ .language_server = .{ "<executable>", "<args>" } };. After adding the entry, rebuild Flow Control using zig build. The new language will appear in flow -l and Flow Control will automatically launch your specified server when opening matching files. You can then create JSON override files for the new language just like any built-in one.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →