GitHub

@@ -29,6 +29,13 @@

2929

#include <signal.h>

3030

#include <setjmp.h>

313132+

#ifdef _WIN32

33+

#include <io.h>

34+

#define isatty(fd) _isatty(fd)

35+

#else

36+

#include <unistd.h>

37+

#endif

38+3239

#include "mirb_completion.h"

33403441

/* obsolete configuration */

@@ -81,6 +88,151 @@

81888289

static const char history_file_name[] = ".mirb_history";

839091+

/* Auto-indent support for GNU readline (not linenoise) */

92+

#if !defined(MRB_USE_LINENOISE) && defined(RL_READLINE_VERSION)

93+

static int mirb_indent_level = 0;

94+

static mrb_bool mirb_use_ansi = FALSE;

95+96+

static int

97+

mirb_startup_hook(void)

98+

{

99+

if (mirb_indent_level > 0) {

100+

int i;

101+

for (i = 0; i < mirb_indent_level; i++) {

102+

rl_insert_text(" ");

103+

}

104+

}

105+

return 0;

106+

}

107+108+

/* Check if terminal supports ANSI escape sequences */

109+

static mrb_bool

110+

supports_escape_sequences(void)

111+

{

112+

const char *term;

113+114+

if (!isatty(fileno(stdout))) return FALSE;

115+116+

term = getenv("TERM");

117+

if (!term || strcmp(term, "dumb") == 0) return FALSE;

118+119+

/* Respect NO_COLOR convention */

120+

if (getenv("NO_COLOR")) return FALSE;

121+122+

return TRUE;

123+

}

124+125+

/* Check if line starts with a mid-block keyword (else, elsif, rescue, ensure, when)

126+

* These keywords should be at the same indent level as the opening keyword */

127+

static mrb_bool

128+

is_midblock_keyword(const char *line)

129+

{

130+

/* skip leading whitespace */

131+

while (*line == ' ' || *line == '\t') line++;

132+133+

if (strncmp(line, "else", 4) == 0 && !ISALNUM(line[4]) && line[4] != '_') return TRUE;

134+

if (strncmp(line, "elsif ", 6) == 0) return TRUE;

135+

if (strncmp(line, "rescue", 6) == 0 && !ISALNUM(line[6]) && line[6] != '_') return TRUE;

136+

if (strncmp(line, "ensure", 6) == 0 && !ISALNUM(line[6]) && line[6] != '_') return TRUE;

137+

if (strncmp(line, "when ", 5) == 0) return TRUE;

138+139+

return FALSE;

140+

}

141+142+

/* Reprint line with corrected indentation using ANSI escapes */

143+

static void

144+

reprint_line_with_indent(int line_num, const char *line, int indent)

145+

{

146+

const char *content = line;

147+

const char *end;

148+

int i;

149+150+

/* Skip original leading whitespace to get actual content */

151+

while (*content == ' ' || *content == '\t') content++;

152+153+

/* Find end of content (exclude trailing newline) */

154+

end = content + strlen(content);

155+

while (end > content && (end[-1] == '\n' || end[-1] == '\r')) end--;

156+157+

/* Move cursor up, clear line, return to beginning */

158+

printf("\033[A\r\033[K");

159+160+

/* Print prompt */

161+

printf("%d* ", line_num);

162+163+

/* Print corrected indentation */

164+

for (i = 0; i < indent; i++) {

165+

printf(" ");

166+

}

167+168+

/* Print content (no newline - cursor stays at end of line) */

169+

printf("%.*s", (int)(end - content), content);

170+171+

/* Move cursor back down to next line */

172+

printf("\n");

173+

fflush(stdout);

174+

}

175+176+

/* Calculate indent level by counting open blocks in code */

177+

static int

178+

calc_indent_level(const char *code)

179+

{

180+

int level = 0;

181+

const char *p = code;

182+183+

while (*p) {

184+

/* Skip strings */

185+

if (*p == '"' || *p == '\'') {

186+

char quote = *p++;

187+

while (*p && *p != quote) {

188+

if (*p == '\\' && p[1]) p++;

189+

p++;

190+

}

191+

if (*p) p++;

192+

continue;

193+

}

194+

/* Skip comments */

195+

if (*p == '#') {

196+

while (*p && *p != '\n') p++;

197+

continue;

198+

}

199+

/* Check for keywords */

200+

if (p == code || !ISALNUM(p[-1])) {

201+

if (strncmp(p, "def ", 4) == 0 ||

202+

strncmp(p, "class ", 6) == 0 ||

203+

strncmp(p, "module ", 7) == 0 ||

204+

strncmp(p, "do\n", 3) == 0 ||

205+

strncmp(p, "do ", 3) == 0 ||

206+

(strncmp(p, "do", 2) == 0 && (p[2] == '\0' || p[2] == '\n')) ||

207+

strncmp(p, "if ", 3) == 0 ||

208+

strncmp(p, "unless ", 7) == 0 ||

209+

strncmp(p, "case ", 5) == 0 ||

210+

strncmp(p, "while ", 6) == 0 ||

211+

strncmp(p, "until ", 6) == 0 ||

212+

strncmp(p, "for ", 4) == 0 ||

213+

strncmp(p, "begin\n", 6) == 0 ||

214+

(strncmp(p, "begin", 5) == 0 && (p[5] == '\0' || p[5] == '\n'))) {

215+

level++;

216+

}

217+

else if (strncmp(p, "end\n", 4) == 0 ||

218+

strncmp(p, "end ", 4) == 0 ||

219+

(strncmp(p, "end", 3) == 0 && (p[3] == '\0' || p[3] == '\n'))) {

220+

if (level > 0) level--;

221+

}

222+

}

223+

/* Check for block opening with { */

224+

if (*p == '{') {

225+

level++;

226+

}

227+

else if (*p == '}') {

228+

if (level > 0) level--;

229+

}

230+

p++;

231+

}

232+

return level;

233+

}

234+

#endif /* !MRB_USE_LINENOISE && RL_READLINE_VERSION */

235+84236

static char *

85237

get_history_path(mrb_state *mrb)

86238

{

@@ -596,10 +748,17 @@ main(int argc, char **argv)

596748

mrb_ccontext_filename(mrb, cxt, "(mirb)");

597749

if (args.verbose) cxt->dump_result = TRUE;

598750599-

/* Setup tab completion */

751+

/* Setup tab completion and auto-indent */

600752

#ifdef MRB_USE_READLINE

601753

#ifndef MRB_USE_LINENOISE

602754

mirb_setup_readline_completion(mrb, cxt);

755+

#if defined(RL_READLINE_VERSION)

756+

/* Only enable auto-indent for interactive input */

757+

if (isatty(fileno(stdin))) {

758+

rl_startup_hook = mirb_startup_hook;

759+

mirb_use_ansi = supports_escape_sequences();

760+

}

761+

#endif

603762

#else

604763

mirb_setup_linenoise_completion(mrb, cxt);

605764

#endif

@@ -661,6 +820,10 @@ main(int argc, char **argv)

661820

{

662821

char prompt[32];

663822

snprintf(prompt, sizeof(prompt), "%d%c ", line_num, code_block_open ? '*' : '>');

823+

#if !defined(MRB_USE_LINENOISE) && defined(RL_READLINE_VERSION)

824+

/* Set indent level for auto-indent on continuation lines */

825+

mirb_indent_level = code_block_open ? calc_indent_level(ruby_code) : 0;

826+

#endif

664827

line = MIRB_READLINE(prompt);

665828

}

666829

signal(SIGINT, SIG_DFL);

@@ -689,6 +852,20 @@ main(int argc, char **argv)

689852

continue;

690853

}

691854

strcat(ruby_code, last_code_line);

855+

#if !defined(MRB_USE_LINENOISE) && defined(RL_READLINE_VERSION)

856+

/* Check if indent level decreased or mid-block keyword needs dedent */

857+

if (mirb_use_ansi && mirb_indent_level > 0) {

858+

int new_level = calc_indent_level(ruby_code);

859+

if (new_level < mirb_indent_level) {

860+

/* Block closed (end, }) - use new lower level */

861+

reprint_line_with_indent(line_num - 1, last_code_line, new_level);

862+

}

863+

else if (is_midblock_keyword(last_code_line)) {

864+

/* Mid-block keyword (else, elsif, rescue, ensure, when) - dedent by 1 */

865+

reprint_line_with_indent(line_num - 1, last_code_line, mirb_indent_level - 1);

866+

}

867+

}

868+

#endif

692869

}

693870

else {

694871

if (check_keyword(last_code_line, "quit") || check_keyword(last_code_line, "exit")) {

Read the original on github.com ↗