Skip to main content
Rode provides a modern editing experience with features designed for productivity and comfort.

Multi-Tab Interface

Work with multiple files simultaneously using Rode’s tab system.

Tab Management

Open Files

Click files in the sidebar, use fuzzy finder, or press Cmd+N for a new file

Switch Tabs

Click tab headers to switch between open files

Close Tabs

Click the close button or press Cmd+W to close the active tab

Modified Indicator

Tabs show a dot (●) when the file has unsaved changes

Tab Types

Rode supports two types of tabs:
pub enum TabKind {
    Editor {
        content: Content,
        modified: bool,
        scroll_line: usize,
    },
    Preview {
        md_items: Vec<markdown::Item>,
    },
}
Editor tabs contain editable text with syntax highlighting and modification tracking. Preview tabs render Markdown files with formatted output.

Keyboard Shortcuts

ShortcutAction
Cmd+NCreate new file
Cmd+WClose active tab
Cmd+SSave file
Cmd+Shift+SSave as (new location)

Syntax Highlighting

Rode uses a VSCode-compatible syntax highlighter for accurate, beautiful code rendering.

Supported Languages

The highlighter detects file types by extension:
  • Rust (.rs)
  • Python (.py)
  • JavaScript (.js, .jsx)
  • TypeScript (.ts, .tsx)
  • Go (.go)
  • Java (.java)
  • C/C++ (.c, .cpp, .h)
  • Ruby (.rb)
  • PHP (.php)
let editor = TextEditor::new(content)
    .on_action(Message::EditorAction)
    .key_binding(editor_key_bindings)
    .highlight_with::<VscodeHighlighter>(
        Settings {
            extension: extension.to_string(),
        },
        |highlight, _theme| highlight.to_format(),
    )
    .style(text_editor_style)
    .padding(iced::Padding { top: 4.0, right: 4.0, bottom: 4.0, left: 4.0 })
    .height(Length::Fill);

Line Numbers & Gutter

Rode displays line numbers in a gutter on the left side of the editor.

Gutter Features

  • Current Line Highlighting: Active line number shown in bright color
  • Visible Window: Shows 60 lines at a time for performance
  • Overflow Indicators: Displays ... when content extends beyond viewport
  • Smart Scrolling: Automatically adjusts to keep cursor visible
pub const GUTTER_VISIBLE_LINES: usize = 60;

pub fn create_editor<'a>(
    content: &'a Content,
    extension: &str,
    current_line: usize,
    scroll_line: usize,
) -> Element<'a, Message> {
    let total_lines = content.line_count().max(1);
    let active_line = current_line.clamp(1, total_lines);
    let max_start_line = total_lines.saturating_sub(GUTTER_VISIBLE_LINES - 1).max(1);
    let start_line = scroll_line.clamp(1, max_start_line);
    let end_line = (start_line + GUTTER_VISIBLE_LINES - 1).min(total_lines);

    let mut gutter_lines = Vec::with_capacity(end_line - start_line + 3);

    if start_line > 1 {
        gutter_lines.push(
            container(text("...").size(12).color(theme().text_dim))
                .width(Length::Fixed(52.0))
                .padding(iced::Padding { top: 0.0, right: 8.0, bottom: 0.0, left: 0.0 })
                .align_right(Length::Fixed(52.0))
                .into()
        );
    }

    for line in start_line..=end_line {
        let is_active = line == active_line;
        gutter_lines.push(
            container(
                text(format!("{line:>4}"))
                    .size(12)
                    .color(if is_active { theme().text_primary } else { theme().text_dim })
            )
            .width(Length::Fixed(52.0))
            .padding(iced::Padding { top: 0.0, right: 8.0, bottom: 0.0, left: 0.0 })
            .align_right(Length::Fixed(52.0))
            .into()
        );
    }
    // ...
}

Cursor Position & Status

The status bar displays your current position in the file. Status bar shows:
  • Current file name
  • Line number (Ln)
  • Column number (Col)

Custom Key Bindings

Rode extends standard editing with smart key bindings.

Enhanced Delete Operations

  • Backspace: Delete character to the left
  • Cmd+Backspace: Delete from cursor to start of line
  • Alt+Backspace: Delete word to the left
  • Delete: Delete character to the right
  • Cmd+Delete: Delete from cursor to end of line
  • Alt+Delete: Delete word to the right
fn editor_key_bindings(key_press: KeyPress) -> Option<Binding<Message>> {
    let modifiers = key_press.modifiers;

    match key_press.key.as_ref() {
        Key::Named(key::Named::Backspace) => {
            if modifiers.command() {
                Some(Binding::Sequence(vec![
                    Binding::Select(Motion::Home),
                    Binding::Backspace,
                ]))
            } else if modifiers.alt() {
                Some(Binding::Sequence(vec![
                    Binding::Select(Motion::WordLeft),
                    Binding::Backspace,
                ]))
            } else {
                Binding::from_key_press(key_press)
            }
        }
        Key::Named(key::Named::Delete) => {
            if modifiers.command() {
                Some(Binding::Sequence(vec![
                    Binding::Select(Motion::End),
                    Binding::Delete,
                ]))
            } else if modifiers.alt() {
                Some(Binding::Sequence(vec![
                    Binding::Select(Motion::WordRight),
                    Binding::Delete,
                ]))
            } else {
                Binding::from_key_press(key_press)
            }
        }
        _ => Binding::from_key_press(key_press),
    }
}

Editor Preferences

Customize your editing experience through the settings panel.

Accessing Preferences

Press Cmd+Shift+S to open settings, or use Command Palette → “Settings”.

Available Settings

tab_size
number
default:"4"
Number of spaces per indentation level (1-16)
use_spaces
boolean
default:"true"
Use spaces instead of tab characters for indentation
theme_name
string
default:"Catppuccin Mocha"
Active color theme

Configuration File

Preferences are stored as Lua configuration:
-- Rode Editor Preferences
-- Edit these values to customize your editor

return {
    tab_size = 4,
    use_spaces = true,
    theme_name = "Catppuccin Mocha",
}
pub fn save_preferences(prefs: &EditorPreferences) -> Result<(), std::io::Error> {
    let path = get_preferences_path();
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }
    let content = format!
        r#"-- Rode Editor Preferences
-- Edit these values to customize your editor

return {{
    tab_size = {},
    use_spaces = {},
    theme_name = "{}",
}}
"#,
        prefs.tab_size, prefs.use_spaces, prefs.theme_name,
    );
    let mut file = fs::File::create(path)?;
    file.write_all(content.as_bytes())?;
    Ok(())
}

Autocomplete

Rode features an intelligent autocomplete engine with fuzzy matching, context awareness, and language-specific suggestions.

How Autocomplete Works

The autocomplete engine automatically triggers as you type, analyzing the current context and providing relevant suggestions.

Context-Aware

Understands member access (.) and function calls to suggest appropriate completions

Fuzzy Matching

Matches your partial input against keywords, types, and identifiers

Language Support

Supports Rust, JavaScript, TypeScript, and Python with language-specific keywords

Smart Scoring

Ranks suggestions by relevance using fuzzy scoring and context boosting

Supported Languages

Autocomplete provides language-specific suggestions for:
Keywords: fn, let, mut, pub, struct, enum, impl, trait, match, if, else, loop, while, for, return, break, continue, use, mod, crate, super, self, async, await, const, static, unsafe, extern, type, where, as, ref, move, dynTypes: String, Vec, Option, Result, Box, Rc, Arc, HashMap, HashSet, PathBuf, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, f32, f64, bool, char, str, usize, isize

Suggestion Types

The autocomplete engine categorizes suggestions by their type:
Language-specific keywords like fn, let, const, defIcon: Purple keyword indicator
Built-in and user-defined types like String, Vec, PromiseScoring Boost: 1.2× after type annotations (:, ->)
Detected function identifiers (names followed by ()Detection: Infers from usage patterns in the code
Functions accessed via member syntax (.method())Context: Triggered after . character
Object/struct members accessed via . without function callContext: Triggered after . without ()
Lowercase identifiers detected in the codeDefault: Any identifier not matching other categories
ALL_CAPS identifiers like MAX_SIZEDetection: All uppercase characters with underscores

How It Works

1

Word Detection

As you type, the engine extracts the current word at the cursor position.
pub fn get_current_word(text: &str, cursor_pos: usize) -> (String, usize)
Words consist of alphanumeric characters and underscores.
2

Context Analysis

The engine analyzes surrounding code to understand context:
  • Member access: Detects . before cursor
  • Function call: Detects ( after potential completions
  • Type context: Detects : or -> before cursor
Source: src/autocomplete/context.rs
3

Suggestion Generation

Three types of suggestions are gathered:
  1. Language keywords - From LanguageDefinitions
  2. Built-in types - Language-specific type names
  3. Identifiers - Extracted from the current file
Source: src/autocomplete/engine.rs:140-175
4

Fuzzy Scoring

Each suggestion receives a score based on:
  • Character match positions
  • Consecutive character bonuses
  • Prefix/suffix matching
  • Context relevance boosts
Source: src/autocomplete/scoring.rs
5

Ranking & Filtering

  • Suggestions sorted by score (descending)
  • Duplicates removed
  • Limited to top 20 results
  • Empty matches filtered out
all_suggestions.truncate(20);

Keyboard Navigation

KeyAction
Tab or Down ArrowSelect next suggestion
Shift+Tab or Up ArrowSelect previous suggestion
EnterAccept selected suggestion
EscapeCancel autocomplete

Context Boosting

The autocomplete engine applies context-specific score multipliers:
pub fn apply_context_boost(
    base_score: f64,
    kind: &SuggestionKind,
    context: &CompletionContext,
) -> f64 {
    let mut score = base_score;
    
    if context.after_type_annotation && matches!(kind, SuggestionKind::Type) {
        score *= 1.2;
    }
    
    if context.is_member_access {
        if matches!(kind, SuggestionKind::Method | SuggestionKind::Property) {
            score *= 1.3;
        }
    }
    
    score
}
Source: src/autocomplete/scoring.rs:31-48

Identifier Extraction

The engine maintains a cache of recently used identifiers (up to 100) with recency-based scoring:
language_defs: LanguageDefinitions,
recent_identifiers: Vec<String>,
max_recent: usize,  // Default: 100
Recent identifiers receive a 1.1× score boost for faster access to frequently used names. Source: src/autocomplete/engine.rs:65-94

Language Detection

File extensions are mapped to language identifiers:
ExtensionLanguage
.rsrust
.js, .mjs, .cjsjavascript
.tstypescript
.tsx, .jsxtypescript
.pypython
Source: src/autocomplete/engine.rs:129-138

Implementation Details

pub struct Autocomplete {
    pub active: bool,
    pub suggestions: Vec<Suggestion>,
    pub selected_index: usize,
    pub trigger_position: usize,
    pub prefix: String,
    
    language_defs: LanguageDefinitions,
    recent_identifiers: Vec<String>,
    max_recent: usize,
}
Source: src/autocomplete/engine.rs:11-21
pub struct Suggestion {
    pub text: String,
    pub kind: SuggestionKind,
    pub score: f64,
}

pub enum SuggestionKind {
    Keyword,
    Type,
    Function,
    Method,
    Property,
    Variable,
    Constant,
}
Source: src/autocomplete/types.rs
The autocomplete system is designed to be non-intrusive. It only activates when you’re actively typing and automatically dismisses when you move the cursor or press Escape.

Markdown Preview

Rode can render Markdown files with live preview.

Activating Preview

Press Cmd+Shift+V while viewing a Markdown file to open a preview tab.
Preview tabs are read-only and display formatted Markdown content. Edit the source file to make changes.

Scrolling & Navigation

Rode provides smooth scrolling with keyboard and mouse support.
  • Mouse Wheel: Scroll through content (automatically scaled by 1/5 for precision)
  • Cursor Following: Viewport automatically scrolls to keep cursor visible
  • 60-Line Window: Displays 60 lines at a time for optimal performance
The editor automatically maintains your scroll position when switching between tabs.