lib/textbringer/window.rb in textbringer-0.1.7 vs lib/textbringer/window.rb in textbringer-0.1.8

- old
+ new

@@ -46,13 +46,15 @@ Curses.constants.grep(/\AKEY_/).each do |name| KEY_NAMES[Curses.const_get(name)] = name.slice(/\AKEY_(.*)/, 1).downcase.intern end + @@started = false @@windows = [] @@current = nil @@echo_area = nil + @@has_colors = false def self.windows @@windows end @@ -117,14 +119,46 @@ def self.echo_area @@echo_area end + def self.has_colors=(value) + @@has_colors = value + end + + def self.has_colors? + @@has_colors + end + + def self.colors + Curses.colors + end + + def self.set_default_colors(fg, bg) + Curses.assume_default_colors(Color[fg], Color[bg]) + Window.redraw + end + + def self.load_faces + require_relative "faces/basic" + require_relative "faces/programming" + end + def self.start + if @@started + raise EditorError, "Already started" + end Curses.init_screen Curses.noecho Curses.raw + Curses.nonl + self.has_colors = Curses.has_colors? + if has_colors? + Curses.start_color + Curses.use_default_colors + load_faces + end begin window = Textbringer::Window.new(Window.lines - 1, Window.columns, 0, 0) window.buffer = Buffer.new_buffer("*scratch*") @@windows.push(window) @@ -132,15 +166,22 @@ @@echo_area = Textbringer::EchoArea.new(1, Window.columns, Window.lines - 1, 0) Buffer.minibuffer.keymap = MINIBUFFER_LOCAL_MAP @@echo_area.buffer = Buffer.minibuffer @@windows.push(@@echo_area) + @@started = true yield ensure + @@windows.each do |win| + win.delete + end + @@windows.clear Curses.echo Curses.noraw + Curses.nl Curses.close_screen + @@started = false end end def self.redisplay return if Window.current.has_input? @@ -171,11 +212,12 @@ Curses.cols end def self.resize @@windows.delete_if do |window| - if !window.echo_area? && window.y > Window.lines - 4 + if !window.echo_area? && + window.y > Window.lines - CONFIG[:window_min_height] window.delete true else false end @@ -282,11 +324,11 @@ return "\e" end end KEY_NAMES[key] || key else - key&.encode(Encoding::UTF_8)&.tr("\r", "\n") + key&.encode(Encoding::UTF_8) end end def read_char_nonblock @window.nodelay = true @@ -327,10 +369,44 @@ ensure @window.nodelay = false end end + def highlight + @highlight_on = {} + @highlight_off = {} + return if !@@has_colors || !CONFIG[:syntax_highlight] + syntax_table = @buffer.mode.syntax_table + return if syntax_table.empty? + if @buffer.bytesize < CONFIG[:highlight_buffer_size_limit] + base_pos = @buffer.point_min + s = @buffer.to_s + else + base_pos = @buffer.point + len = columns * (lines - 1) / 2 * 3 + s = @buffer.substring(@buffer.point, @buffer.point + len).scrub("") + end + re_str = syntax_table.map { |name, re| + "(?<#{name}>#{re})" + }.join("|") + re = Regexp.new(re_str) + names = syntax_table.keys + s.scan(re) do + b = base_pos + $`.bytesize + e = b + $&.bytesize + if b < @buffer.point && @buffer.point < e + b = @buffer.point + end + name = names.find { |n| $~[n] } + attributes = Face[name]&.attributes + if attributes + @highlight_on[b] = attributes + @highlight_off[e] = attributes + end + end + end + def redisplay return if @buffer.nil? redisplay_mode_line @buffer.save_point do |saved| if current? @@ -340,12 +416,14 @@ @buffer.point_to_mark(@point_mark) end framer y = x = 0 @buffer.point_to_mark(@top_of_window) + highlight @window.erase @window.setpos(0, 0) + @window.attrset(0) if current? && @buffer.visible_mark && @buffer.point_after_mark?(@buffer.visible_mark) @window.attron(Curses::A_REVERSE) end while !@buffer.end_of_buffer? @@ -366,10 +444,16 @@ @window.attroff(Curses::A_REVERSE) elsif @buffer.point_before_mark?(point) @window.attron(Curses::A_REVERSE) end end + if attrs = @highlight_off[@buffer.point] + @window.attroff(attrs) + end + if attrs = @highlight_on[@buffer.point] + @window.attron(attrs) + end c = @buffer.char_after if c == "\n" @window.clrtoeol break if cury == lines - 2 # lines include mode line @window.setpos(cury + 1, 0) @@ -476,22 +560,63 @@ @buffer.beginning_of_line @top_of_window.location = 0 end def split - if lines < 6 - raise EditorError, "Window too small" - end old_lines = lines new_lines = (old_lines / 2.0).ceil + if new_lines < CONFIG[:window_min_height] + raise EditorError, "Window too small" + end resize(new_lines, columns) new_window = Window.new(old_lines - new_lines, columns, y + new_lines, x) new_window.buffer = buffer i = @@windows.index(self) @@windows.insert(i + 1, new_window) end + def enlarge(n) + if n > 0 + max_height = Window.lines - + CONFIG[:window_min_height] * (@@windows.size - 2) - 1 + new_lines = [lines + n, max_height].min + needed_lines = new_lines - lines + resize(new_lines, columns) + i = @@windows.index(self) + indices = (i + 1).upto(@@windows.size - 2).to_a + + (i - 1).downto(0).to_a + indices.each do |j| + break if needed_lines == 0 + window = @@windows[j] + extended_lines = [ + window.lines - CONFIG[:window_min_height], + needed_lines + ].min + window.resize(window.lines - extended_lines, window.columns) + needed_lines -= extended_lines + end + y = 0 + @@windows.each do |win| + win.move(y, win.x) + y += win.lines + end + elsif n < 0 && @@windows.size > 2 + new_lines = [lines + n, CONFIG[:window_min_height]].max + diff = lines - new_lines + resize(new_lines, columns) + i = @@windows.index(self) + if i < @@windows.size - 2 + window = @@windows[i + 1] + window.move(window.y - diff, window.x) + else + window = @@windows[i - 1] + move(self.y + diff, self.x) + end + window.resize(window.lines + diff, window.columns) + end + end + private def initialize_window(num_lines, num_columns, y, x) @window = Curses::Window.new(num_lines - 1, num_columns, y, x) @mode_line = Curses::Window.new(1, num_columns, y + num_lines - 1, x) @@ -520,11 +645,12 @@ end def redisplay_mode_line @mode_line.erase @mode_line.setpos(0, 0) - @mode_line.attron(Curses::A_REVERSE) + attrs = @@has_colors ? Face[:mode_line].attributes : Curses::A_REVERSE + @mode_line.attrset(attrs) @mode_line.addstr("#{@buffer.name} ") @mode_line.addstr("[+]") if @buffer.modified? @mode_line.addstr("[RO]") if @buffer.read_only? @mode_line.addstr("[#{@buffer.file_encoding.name}/") @mode_line.addstr("#{@buffer.file_format}] ") @@ -538,10 +664,10 @@ end @mode_line.addstr(unicode_codepoint(c)) @mode_line.addstr(" #{line},#{column}") @mode_line.addstr(" (#{@buffer.mode&.name || 'None'})") @mode_line.addstr(" " * (columns - @mode_line.curx)) - @mode_line.attroff(Curses::A_REVERSE) + @mode_line.attrset(0) @mode_line.noutrefresh end def unicode_codepoint(c) if c.nil?