#!/usr/bin/env fancy # -*- set ft=fancy -*- class Fancy Interactive { @@info = <[]> def self [key] { @@info[key] } def self [key]: value { @@info[key]: value } } try { require("readline") } catch LoadError { require("rb-readline/readline") } try { require("rubygems") require("coderay") require(File.expand_path(File.dirname(__FILE__)) ++ "/../ruby_lib/interactive/hilight") Fancy Interactive['coderay]: true } catch LoadError { Fancy Interactive['coderay]: false } ["Welcome to the (still very simple) Fancy REPL", "Fancy #{Fancy VERSION}"] println HISTORY_FILE = File expand_path("~/.fancy_history") HISTORY = [] PROMPT = "ifancy[%d]> " class Prompt { def initialize: @str { @line = 0 } def inc_line { @line = @line + 1 } def aug_line: num { @line = @line + num } def to_s { sprintf(@str, @line) } } class Feature { @@hooks = <[]> read_slots: ('name, 'desc) def initialize: @name will: @desc by: @block { @@hooks[@name]: self } def run: string { @block call: string } def about: name_width (@name size) { sprintf("%-#{name_width}s : %s", @name, @desc) } def Feature match: name str: string { if: (@@hooks[name]) then: |feature| { feature run: string return true } return false } def Feature find: name do: block else: else_block { @@hooks with_value_for_key: name do: block else: else_block } def Feature each: block { @@hooks values sort_by: 'name . each: block } metaclass provides_interface: 'each: extend: Fancy Enumerable } ARGV rest each: |file| { "LOADING: #{file}" println require: file } Console newline; def double_or_empty?: line { (line =~ /^\s*$/) or: (HISTORY [-2] == line) } def load_history { if: (File exists?: HISTORY_FILE) then: { File read: HISTORY_FILE with: |f| { f readlines each: |l| { l = l strip() Readline HISTORY <<(l) HISTORY << l } } } else: { File touch: HISTORY_FILE } } def save_history { puts("saving history") unless: @history_saved do: { File write: HISTORY_FILE with: |f| { HISTORY last: 100 . each: |l| { f writeln: l } } @history_saved = true } } def Readline completion: block { Readline completion_proc=(block) } Readline completion_append_character=(" ") Readline completion: |str| { receiver = nil end_idx = -2 methods = [] 3 times: { try { receiver = Readline line_buffer() split: " " . [[0,end_idx]] . join: " " . eval methods = receiver methods } catch Fancy Parser ParseError => e { end_idx = end_idx - 1 } catch Exception => e { methods = Object instance_methods } } completions = [] try { regexp = Regexp new(str) completions = methods select: @{ =~ regexp } . map: @{ gsub(/^:/, "") } } catch RegexpError { } finally { completions } } def read_line: prompt { try { return Readline readline(prompt to_s, true) } catch ArgumentError { return "" } } load_history at_exit() { save_history } bnd = binding() prompt = Prompt new: PROMPT Feature new: "exit" will: "Exit the shell" by: { System exit } Feature new: "quit" will: "Exit the shell" by: { System exit } Feature new: "?" will: "Give information on a command" by: |line| { feature_name = line words[1] if: feature_name then: { Feature find: feature_name do: @{ about println } else: { "Sorry, #{feature_name} is not a feature that is loaded" println } } else: { max_width = Feature map: @{ name size } . max Feature each: @{ about: max_width . println } } } Feature new: "#{" will: "Create multiline capabilities" by: { line = read_line: "-> " line_s = "" until: { line =~ /^\#}$/ == 0} do: { line_s = line_s ++ line ++ "\n" line = read_line: "-> " } try { Fancy eval: line_s binding: bnd . inspect println prompt aug_line: (line_s split: "\n" . size) } catch Exception => e { e message println } } loop: { line = read_line: prompt { System exit } unless: line { next } if: (Feature match: (line words[0]) str: line) HISTORY << line if: (double_or_empty?: line) then: { Readline::HISTORY pop() HISTORY pop() } try { if: (Fancy Interactive['coderay]) then: { "=> #{CodeRay.scan((Fancy eval: line binding: bnd . inspect), 'fancy).term()}" println } else: { "=> #{Fancy eval: line binding: bnd . inspect}" println } prompt inc_line } catch Exception => e { e message println } }