Since presym is now mandatory, mruby.h includes presym.h so that
MRB_SYM() macros are available everywhere without explicit include.
Remove redundant #include <mruby/presym.h> from all source files.
Co-authored-by: Claude <noreply@anthropic.com>
Remove redundant local ISSPACE/ISALNUM definitions from
mirb_completion.c and unused ctype.h includes from both
mirb_completion.c and mirb.c. The locale-independent macros
from mruby.h are already available via <mruby.h>.
Co-authored-by: Claude <noreply@anthropic.com>
Replace the strncmp() if-else chain for block-opening and closing
keywords with a data-driven indent_table, matching the existing
dedent_table pattern. Also use mirb_is_word_char() for the word
boundary check.
Co-authored-by: Claude <noreply@anthropic.com>
Replace duplicate line-joining logic in mirb_buffer_delete_back()
and mirb_buffer_delete_forward() with a shared helper that appends
a line's content to the previous line, then removes it.
Co-authored-by: Claude <noreply@anthropic.com>
Replace three identical 7-line blocks that grow the lines array
with a single buffer_ensure_line_cap() helper function, matching
the existing line_ensure_cap() naming pattern.
Co-authored-by: Claude <noreply@anthropic.com>
Replace three separate global contexts (g_readline_ctx,
g_linenoise_ctx, g_editor_ctx) with a single g_ctx and shared
init_completion_ctx() helper. Consolidate the three identical
cleanup functions into mirb_cleanup_completion().
Co-authored-by: Claude <noreply@anthropic.com>
Replace the if-else chain with a data table that encodes each
dedent keyword, its valid delimiters, and whether it can appear
at end of line.
Co-authored-by: Claude <noreply@anthropic.com>
Move is_word_char() to mirb_buffer.h as mirb_is_word_char() static
inline, removing duplicate definitions from mirb_buffer.c and
mirb_highlight.c. Move COLOR_RESET to mirb_highlight.h, removing
the duplicate from mirb_editor.c.
Co-authored-by: Claude <noreply@anthropic.com>
Replace the cleanup() function and duplicated end-of-main cleanup
with a single goto cleanup label. This also fixes a minor resource
leak where cxt was not freed when library loading failed.
Co-authored-by: Claude <noreply@anthropic.com>
Move the Ruby keyword array from static definitions in both
mirb_highlight.c and mirb_completion.c to a single shared
mirb_keywords[] defined in mirb_highlight.c and declared in
mirb_highlight.h.
Co-authored-by: Claude <noreply@anthropic.com>
Extract three helpers (calc_expected_indent, adjust_line_indent,
insert_indent_spaces) to eliminate repeated indent computation and
whitespace adjustment code in perform_dedent, reindent_line,
handle_tab_indent, and the Enter key handler.
Co-authored-by: Claude <noreply@anthropic.com>
Zero-initialize the mirb_editor struct to prevent highlight.enabled
from containing garbage values when stdin is not a tty (e.g. in
bintest). Without this, ANSI color codes could be emitted in
non-interactive mode, breaking output string matching in tests.
Co-authored-by: Claude <noreply@anthropic.com>
Use syntax highlighter for result values instead of single color.
Add support for hash key symbol syntax (e.g., `a:` in `{a: 1}`).
Co-authored-by: Claude <noreply@anthropic.com>
Add syntax highlighting to mirb's multi-line editor with support for:
- keywords (def, if, class, end, etc.) in magenta
- strings ("...", '...', %q{...}) in green
- comments (#...) in gray
- numbers (42, 3.14, 0xff) in cyan
- symbols (:foo) in yellow
- constants (Array, Foo) in bold yellow
- instance variables (@var) in blue
- global variables ($var) in bold blue
Features:
- auto-detects light/dark theme via COLORFGBG env var
- MIRB_THEME=light/dark for explicit override
- method calls like obj.class correctly not highlighted as keywords
- enabled automatically when terminal supports color
Co-authored-by: Claude <noreply@anthropic.com>
Use _WIN32 instead of _MSC_VER to provide strndup implementation
for all Windows compilers including MinGW/MSYS.
Co-authored-by: Claude <noreply@anthropic.com>
- Define strdup as _strdup on MSVC to avoid deprecation warning
- Add strndup implementation for Windows (not available in MSVC)
Co-authored-by: Claude <noreply@anthropic.com>
Consolidate duplicated blank line check logic from two places in the
ENTER key handler into a single helper function.
Co-authored-by: Claude <noreply@anthropic.com>
move buffer_to_string_upto_line() from mirb_editor.c to mirb_buffer.c
as a public API. mirb_buffer_to_string() now delegates to this function.
this eliminates code duplication and provides proper module encapsulation.
Co-authored-by: Claude <noreply@anthropic.com>
consolidate duplicated dedenting keyword detection logic that was
repeated in reindent_line(), handle_tab_indent(), and handle_key().
the helper checks for end, else, elsif, when, in, rescue, ensure, and }.
Co-authored-by: Claude <noreply@anthropic.com>
add support for automatic dedentation when typing 'in' at the
beginning of a line, matching the behavior of 'when' for pattern
matching case/in expressions.
Co-authored-by: Claude <noreply@anthropic.com>
Before inserting a newline, re-indent the current line to match
the expected indent level. This fixes cases where the user typed
with incorrect indentation.
Co-authored-by: Claude <noreply@anthropic.com>
When TAB triggers auto-indentation, preserve the cursor's relative
position within the line instead of moving it to the indent boundary.
Co-authored-by: Claude <noreply@anthropic.com>
Instead of just removing 2 spaces, perform_dedent() now calculates
the expected indent level from previous lines and aligns to that.
Co-authored-by: Claude <noreply@anthropic.com>
Extend auto-dedent to trigger when typing dedent keywords, not just
end and }. Now dedent occurs when completing: else, elsif, when,
rescue, ensure.
Co-authored-by: Claude <noreply@anthropic.com>
When splitting a line with Enter, check if the new line starts with
a dedenting keyword (end, else, elsif, when, rescue, ensure, }) and
reduce indentation by one level.
Co-authored-by: Claude <noreply@anthropic.com>
TAB now performs auto-indentation instead of completion when:
- cursor is at start of line
- cursor is at end of line
- character before cursor is whitespace
Auto-indent calculates expected indent level from previous lines
and adjusts current line. Dedenting keywords (end, else, elsif,
when, rescue, ensure, }) reduce indent by one level.
Co-authored-by: Claude <noreply@anthropic.com>
when pressing Ctrl+K on an empty line, delete the entire line instead
of doing nothing. this makes it easier to clean up empty lines while
editing multi-line input.
Co-authored-by: Claude <noreply@anthropic.com>
pressing Enter in the middle of multi-line input now always inserts
a new line instead of evaluating, even if the code is syntactically
complete. evaluation only occurs when cursor is at the end of the
last line.
Co-authored-by: Claude <noreply@anthropic.com>
- restore mirb_completion.c/h from before readline removal
- add editor adapter for tab completion (mirb_setup_editor_completion,
mirb_get_completions, mirb_free_completions)
- add TAB key handling in mirb_editor.c
- fix string literal completion: properly detect when cursor is outside
a string by scanning forward, allow string/array/hash literals as
safe receivers for method completion
Co-authored-by: Claude <noreply@anthropic.com>
- fix Enter in middle of line with trailing blank continuation line:
now properly splits the line and removes redundant blank line
- fix auto-indentation when inserting in middle of existing code:
calculate indent from lines up to cursor, not entire buffer
- add mirb_buffer_delete_line() for removing lines from buffer
Co-authored-by: Claude <noreply@anthropic.com>
Previously, all continuation lines showed the same line number (e.g.,
"1*" for every line). Now each line shows its actual line number:
1> class Foo
2* def bar
3* end
4* end
Add mirb_editor_set_prompt_format() which accepts printf-style format
strings (e.g., "%d> ", "%d* ") and calculates the correct prompt length
for each line to ensure proper cursor positioning.
Co-authored-by: Claude <noreply@anthropic.com>
Add in-memory command history for mirb sessions:
- Up arrow on first line: navigate to older history entries
- Down arrow on last line: navigate to newer history entries
- Current input is preserved when browsing and restored when
navigating past the newest entry
- History uses a circular buffer (100 entries max)
- Duplicate consecutive entries are not added
Co-authored-by: Claude <noreply@anthropic.com>
Remove readline/linenoise dependency and implement custom multi-line
editor with:
- Terminal raw mode handling (POSIX termios)
- Multi-line buffer with cursor navigation
- Auto-indentation for Ruby blocks
- Auto-dedentation when typing 'end' or '}'
- Natural terminal scrolling behavior
- Emacs-style keybindings (Ctrl+A/E/K/U/W/Y, Alt+B/F/D)
This eliminates GPL licensing concerns from readline while providing
better multi-line editing than the previous single-line implementation.
The MRUBY_MIRB_READLINE environment variable is no longer supported
as readline integration has been completely removed; ref #6626
Co-authored-by: Claude <noreply@anthropic.com>
Add ANSI color support to mirb for better visual distinction:
- green prompts (both ready '>' and continuation '*')
- red error messages (syntax errors, runtime errors, warnings)
- bold result indicator ('=>')
Colors are automatically disabled when:
- output is not a TTY
- TERM is unset or "dumb"
- NO_COLOR environment variable is set
Co-authored-by: Claude <noreply@anthropic.com>
- automatically indent continuation lines based on block depth
- detect block-opening keywords (def, class, if, do, etc.) and braces
- use ANSI escape sequences to fix indentation for:
- block-closing keywords (end, })
- mid-block keywords (else, elsif, rescue, ensure, when)
- only active for interactive TTY input with ANSI support
- requires GNU readline (not available with linenoise)
Co-authored-by: Claude <noreply@anthropic.com>
fixed a bug where tab completion on complex expressions like "d.new(1).a"
would corrupt local variables, causing them to become nil.
the root cause was that evaluating complex receiver expressions during tab
completion ran mrb_vm_run() without proper stack management (stack_keep)
and environment adjustment that mirb's main REPL loop performs. this
corrupted the local variable storage.
the fix restricts tab completion to only evaluate simple receiver
expressions (variable/constant names without operators or method calls).
complex expressions are skipped for completion. this means:
- works: d.<tab> completes methods of variable d
- works: String.<tab> completes methods of constant String
- skipped: d.new(1).<tab> provides no completion
this is a reasonable trade-off that prevents the corruption bug while
still supporting the most common completion scenarios.
also updated mirb_eval_receiver() to use the compiler context for proper
local variable resolution, with argument order matching mrb_parse_string.
Co-authored-by: Claude <noreply@anthropic.com>
implements context-aware tab completion for mirb supporting all readline
variants (GNU readline, libedit, linenoise) with graceful degradation
when no readline library is available.
completion features:
- method names on objects (e.g., "hello".re<Tab> completes to reverse, replace)
- local variables from compiler context
- global variables via Ruby introspection
- constants and class names
- Ruby keywords
architecture:
- core completion engine is library-agnostic
- thin adapters for readline/libedit and linenoise
- context detection based on cursor position analysis
- safe receiver evaluation with exception handling
- proper word break characters so "String.new" works correctly
implementation adds:
- mirb_completion.h: interface definitions and data structures
- mirb_completion.c: complete implementation (~670 lines)
- mirb.c: integration with setup/cleanup calls
Co-authored-by: Claude <noreply@anthropic.com>
addresses #6626 where users building portable binaries need explicit control
over readline detection instead of auto-detection.
MRUBY_MIRB_READLINE values:
auto (default) - auto-detect: try readline, then edit, then linenoise
readline, gnu - force GNU readline only
edit, libedit - force libedit only
linenoise - force linenoise only
none, off, false, disabled - use plain input mode (no readline)
close#6626
Co-authored-by: Claude <noreply@anthropic.com>
runtime errors now distinguish between:
- errors in current input: show relative line number
- errors from previously defined methods: show method context
examples:
1> a.foo
line 1: undefined method 'a' for Object (NoMethodError)
1> def foo
2* bar
3* end
1> foo
(mirb):in foo: undefined method 'bar' for Object (NoMethodError)
this provides better context since method name is more useful
than line number for errors in previously defined code
Co-authored-by: Claude <noreply@anthropic.com>
syntax errors now show:
- line:column format with relative line numbers (matching prompt)
- source line from user input
- caret indicator pointing to error position
example:
1> x = @@@
line 1:6: syntax error, unexpected invalid token
x = @@@
^
multi-line example:
1> class Foo
2* def bar
3* x = @@@
line 3:6: syntax error, unexpected invalid token
x = @@@
^
Co-authored-by: Claude <noreply@anthropic.com>
add minimal line number decoration to prompts to help track position
within multi-line code blocks. format is 'N>' for initial line and
'N*' for continuation lines. line counter resets after each complete
evaluation for clarity and minimal visual noise.
example:
1> def foo
2* x = 1
3* end
=> :foo
1> 1 + 1
=> 2
Co-authored-by: Claude <noreply@anthropic.com>
since all current uses check for failure (!MRB_OPEN_SUCCESS), add
MRB_OPEN_FAILURE() as the primary macro for better readability. define
MRB_OPEN_SUCCESS() in terms of MRB_OPEN_FAILURE() to avoid duplication
and optimize the common case. update all usage sites to use the clearer
MRB_OPEN_FAILURE() form.
Co-authored-by: Claude <noreply@anthropic.com>
made mrb_print_error() handle NULL by printing "Failed to allocate
mrb_state" when mrb is NULL. since mrb_close() already handles NULL,
this allows simplified error checking pattern:
if (!MRB_OPEN_SUCCESS(mrb)) {
mrb_print_error(mrb); // handles NULL
mrb_close(mrb); // handles NULL
return EXIT_FAILURE;
}
updated all binary tools (mruby, mirb, mrdb, mrbtest) to use this
simplified pattern, removing nested if checks.
Co-authored-by: Claude <noreply@anthropic.com>