textbringer 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: acc4214c3d7c5b5535435505b2504db1f80d46ba
4
- data.tar.gz: e0ad741989368ad91ce5d2b5100896297d28a059
3
+ metadata.gz: b6dafe6a94443fe1e79c7a9316973fc43ec01525
4
+ data.tar.gz: 8332b5d2ef6dd7cbb4df030b9e2ce79a4551be83
5
5
  SHA512:
6
- metadata.gz: f4adfeefebf234a5329c2bfda93f8d9ffe4a83fc029a57579fa8e72d58802aa92be479949a1243f540047ad495efa6507dbbd1d1f61cd5c27e8e3f17225160f3
7
- data.tar.gz: 8edab0edf73fbff79c8a16b62a9863a6aa5ee5867abbc494e9bb261c2885fc5e277ac9957731e80adf8806fbcfa15a8e11096094259f0e4ebf7390ba16e3a21e
6
+ metadata.gz: 5d7687449b608e273a1a0a11155e80e07543095e0bf73deab13740767166274590803d28e203a5816a6526d44a25bf49ac36855deecc1833a007b4a84525b933
7
+ data.tar.gz: 4cf790621ce7c55960cc7e0087fb6dac140280d60adf20e2e0b9b88f69eef252226cf9bdbf975614bdf56658cbc90de074f568c4a9775fb0c983206fa5951293
@@ -1,12 +1,25 @@
1
1
  sudo: false
2
+ os:
3
+ - linux
4
+ - osx
2
5
  language: ruby
3
6
  rvm:
4
- - 2.3.3
5
- - 2.4.0
7
+ - 2.3.3
8
+ - 2.4.0
9
+ - ruby-head
10
+ matrix:
11
+ allow_failures:
12
+ - os: osx
13
+ - rvm: ruby-head
14
+ fast_finish: true
6
15
  cache: bundler
7
16
  script:
8
- - xvfb-run bundle exec rake test
9
- - bundle exec bundle-audit check --update
17
+ - if [ "$TRAVIS_OS_NAME" == "linux" ]; then
18
+ xvfb-run bundle exec rake test;
19
+ else
20
+ bundle exec rake test;
21
+ fi
22
+ - bundle exec bundle-audit check --update
10
23
  env:
11
24
  global:
12
25
  UPLOAD_TO_CODECOV: 1
@@ -14,3 +27,7 @@ notifications:
14
27
  email:
15
28
  on_success: never
16
29
  on_failure: always
30
+ addons:
31
+ apt:
32
+ packages:
33
+ - xclip
data/CHANGES.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.1.8
2
+
3
+ * Support syntax highlighting.
4
+ * Add the commands grep, gsub, and enlarge_window.
5
+ * Don't translate CR into LF.
6
+
1
7
  ## 0.1.7
2
8
 
3
9
  * Support EditorConfig.
data/README.md CHANGED
@@ -11,7 +11,9 @@ text editor.
11
11
 
12
12
  ## Demo
13
13
 
14
- [![asciicast](https://asciinema.org/a/100156.png)](https://asciinema.org/a/100156)
14
+ * FizzBuzz: https://asciinema.org/a/103357
15
+ * Ruby Programming: https://asciinema.org/a/100156
16
+ * Japanese Text Editing: https://asciinema.org/a/100166
15
17
 
16
18
  ## WARNING
17
19
 
@@ -25,6 +27,12 @@ minor versions.
25
27
 
26
28
  $ gem install textbringer
27
29
 
30
+ You need ncursesw to use multibyte characters.
31
+ Install ncursesw before installing curses.gem, on which textbringer depends.
32
+
33
+ $ sudo apt-get install libncursesw5-dev
34
+ $ gem install curses
35
+
28
36
  ## Usage
29
37
 
30
38
  $ textbringer
@@ -33,7 +33,7 @@ Window.start do
33
33
  end
34
34
  end
35
35
  rescue Exception => e
36
- handle_exception(e)
36
+ show_exception(e)
37
37
  end
38
38
  Window.redisplay
39
39
  begin
@@ -5,6 +5,8 @@ require_relative "textbringer/buffer"
5
5
  require_relative "textbringer/window"
6
6
  require_relative "textbringer/keymap"
7
7
  require_relative "textbringer/utils"
8
+ require_relative "textbringer/color"
9
+ require_relative "textbringer/face"
8
10
  require_relative "textbringer/commands"
9
11
  require_relative "textbringer/commands/buffers"
10
12
  require_relative "textbringer/commands/windows"
@@ -162,7 +162,7 @@ module Textbringer
162
162
  end
163
163
 
164
164
  def expand_tab(s)
165
- # TOOD: Support multibyte characters
165
+ # TODO: Support multibyte characters
166
166
  tw = self[:tab_width]
167
167
  fmt = "A#{tw}"
168
168
  s.b.gsub(/([^\t]{#{tw}})|([^\t]*)\t/n) {
@@ -481,7 +481,8 @@ module Textbringer
481
481
  @goal_column = nil
482
482
  end
483
483
 
484
- def insert(s, merge_undo = false)
484
+ def insert(x, merge_undo = false)
485
+ s = x.to_s
485
486
  check_read_only_flag
486
487
  pos = @point
487
488
  size = s.bytesize
@@ -504,6 +505,7 @@ module Textbringer
504
505
  end
505
506
  @modified = true
506
507
  @goal_column = nil
508
+ self
507
509
  end
508
510
 
509
511
  def newline
@@ -1185,6 +1187,14 @@ module Textbringer
1185
1187
  end
1186
1188
  end
1187
1189
 
1190
+ def gsub(*args, &block)
1191
+ s = to_s.gsub(*args, &block)
1192
+ delete_region(point_min, point_max)
1193
+ insert(s)
1194
+ merge_undo(2)
1195
+ self
1196
+ end
1197
+
1188
1198
  private
1189
1199
 
1190
1200
  def adjust_gap(min_size = 0, pos = @point)
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "curses"
4
+
5
+ module Textbringer
6
+ module Color
7
+ BASIC_COLORS = {
8
+ "default" => -1,
9
+ "black" => Curses::COLOR_BLACK,
10
+ "red" => Curses::COLOR_RED,
11
+ "green" => Curses::COLOR_GREEN,
12
+ "yellow" => Curses::COLOR_YELLOW,
13
+ "blue" => Curses::COLOR_BLUE,
14
+ "magenta" => Curses::COLOR_MAGENTA,
15
+ "cyan" => Curses::COLOR_CYAN,
16
+ "white" => Curses::COLOR_WHITE,
17
+ "brightblack" => 8,
18
+ "brightred" => 9,
19
+ "brightgreen" => 10,
20
+ "brightyellow" => 11,
21
+ "brightblue" => 12,
22
+ "brightmagenta" => 13,
23
+ "brightcyan" => 14,
24
+ "brightwhite" => 15
25
+ }
26
+
27
+ RGBColor = Struct.new(:r, :g, :b, :number)
28
+
29
+ ADDITIONAL_COLORS = []
30
+ rgb_values = [0, 0x5F, 0x87, 0xAF, 0xD7, 0xFF]
31
+ rgb_values.product(rgb_values, rgb_values).each_with_index do
32
+ |(r, g, b), i|
33
+ ADDITIONAL_COLORS << RGBColor.new(r, g, b, 16 + i)
34
+ end
35
+ [
36
+ 0x08, 0x12, 0x1c, 0x26, 0x30, 0x3A, 0x44, 0x4E, 0x58, 0x62, 0x6C, 0x76,
37
+ 0x80, 0x8a, 0x94, 0x9E, 0xA8, 0xB2, 0xBC, 0xC6, 0xD0, 0xDA, 0xE4, 0xEE
38
+ ].each_with_index do |v, i|
39
+ ADDITIONAL_COLORS << RGBColor.new(v, v, v, 232 + i)
40
+ end
41
+
42
+ def self.[](name)
43
+ n = find_color_number(name)
44
+ if n < Window.colors
45
+ n
46
+ else
47
+ -1
48
+ end
49
+ end
50
+
51
+ def self.find_color_number(name)
52
+ if name.is_a?(Integer)
53
+ return name
54
+ end
55
+ case name
56
+ when /\A\#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})\z/
57
+ r = $1.to_i(16)
58
+ g = $2.to_i(16)
59
+ b = $3.to_i(16)
60
+ ADDITIONAL_COLORS.sort_by { |c|
61
+ (r - c.r) ** 2 + (g - c.g) ** 2 + (b - c.b) ** 2
62
+ }.first.number
63
+ else
64
+ unless BASIC_COLORS.key?(name)
65
+ raise EditorError, "No such color: #{name}"
66
+ end
67
+ BASIC_COLORS[name]
68
+ end
69
+ end
70
+ end
71
+ end
@@ -16,6 +16,7 @@ module Textbringer
16
16
  def define_command(name, &block)
17
17
  Commands.send(:define_method, name, &block)
18
18
  @@command_list << name if !@@command_list.include?(name)
19
+ name
19
20
  end
20
21
  module_function :define_command
21
22
 
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ module Clipboard
4
+ @implementation = nil
5
+ end
6
+
3
7
  require "clipboard"
4
8
 
5
9
  module Textbringer
@@ -14,7 +14,6 @@ module Textbringer
14
14
  index: nil,
15
15
  tag_mark_stack: []
16
16
  }
17
- TAG_MARK_LIMIT = 16
18
17
 
19
18
  define_command(:find_tag) do |next_p = current_prefix_arg|
20
19
  tags = get_tags
@@ -111,7 +110,7 @@ module Textbringer
111
110
 
112
111
  def push_tag_mark_and_find_file(file)
113
112
  tag_mark_stack = CTAGS[:tag_mark_stack]
114
- if tag_mark_stack.size == TAG_MARK_LIMIT
113
+ if tag_mark_stack.size == CONFIG[:tag_mark_limit]
115
114
  tag_mark_stack.shift.delete
116
115
  end
117
116
  tag_mark_stack.push(Buffer.current.new_mark)
@@ -227,16 +227,21 @@ module Textbringer
227
227
 
228
228
  define_command(:shell_execute) do
229
229
  |cmd = read_from_minibuffer("Shell execute: "),
230
- buffer_name = "*Shell output*"|
230
+ buffer_name: "*Shell output*",
231
+ mode: FundamentalMode|
231
232
  buffer = Buffer.find_or_new(buffer_name)
232
233
  switch_to_buffer(buffer)
234
+ buffer.apply_mode(mode)
233
235
  buffer.read_only = false
234
236
  buffer.clear
235
237
  Window.redisplay
236
238
  signals = [:INT, :TERM, :KILL]
237
239
  begin
238
240
  opts = /mswin32|mingw32/ =~ RUBY_PLATFORM ? {} : {pgroup: true}
239
- Open3.popen2e(cmd, opts) do |input, output, wait_thread|
241
+ if CONFIG[:shell_file_name]
242
+ cmd = [CONFIG[:shell_file_name], CONFIG[:shell_command_switch], cmd]
243
+ end
244
+ Open3.popen2e(*cmd, opts) do |input, output, wait_thread|
240
245
  input.close
241
246
  loop do
242
247
  status = output.wait_readable(0.5)
@@ -278,5 +283,11 @@ module Textbringer
278
283
  buffer.read_only = true
279
284
  end
280
285
  end
286
+
287
+ define_command(:grep) do
288
+ |cmd = read_from_minibuffer("Grep: ",
289
+ initial_value: CONFIG[:grep_command] + " ")|
290
+ shell_execute(cmd, buffer_name: "*grep*", mode: BacktraceMode)
291
+ end
281
292
  end
282
293
  end
@@ -35,6 +35,10 @@ module Textbringer
35
35
  Window.other_window
36
36
  end
37
37
 
38
+ define_command(:enlarge_window) do |n = number_prefix_arg|
39
+ Window.current.enlarge(n)
40
+ end
41
+
38
42
  define_command(:switch_to_buffer) do
39
43
  |buffer_name = read_buffer("Switch to buffer: ")|
40
44
  if buffer_name.is_a?(Buffer)
@@ -8,6 +8,13 @@ module Textbringer
8
8
  tab_width: 8,
9
9
  indent_tabs_mode: false,
10
10
  case_fold_search: true,
11
- buffer_dump_dir: File.expand_path("~/.textbringer/buffer_dump")
11
+ buffer_dump_dir: File.expand_path("~/.textbringer/buffer_dump"),
12
+ tag_mark_limit: 16,
13
+ window_min_height: 4,
14
+ syntax_highlight: true,
15
+ highlight_buffer_size_limit: 102400,
16
+ shell_file_name: ENV["SHELL"],
17
+ shell_command_switch: "-c",
18
+ grep_command: "grep -nH -e"
12
19
  }
13
20
  end
@@ -7,7 +7,7 @@ module Textbringer
7
7
  class Controller
8
8
  attr_accessor :this_command, :last_command, :overriding_map
9
9
  attr_accessor :prefix_arg, :current_prefix_arg
10
- attr_reader :last_key, :recursive_edit_level
10
+ attr_reader :key_sequence, :last_key, :recursive_edit_level
11
11
 
12
12
  @@current = nil
13
13
 
@@ -114,7 +114,7 @@ module Textbringer
114
114
  "<#{key}>"
115
115
  when "\e"
116
116
  "ESC"
117
- when "\n"
117
+ when "\C-m"
118
118
  "RET"
119
119
  when /\A[\0-\b\v-\x1f\x7f]\z/
120
120
  "C-" + (key.ord ^ 0x40).chr.downcase
@@ -123,14 +123,6 @@ module Textbringer
123
123
  end
124
124
  end
125
125
 
126
- private
127
-
128
- def key_binding(key_sequence)
129
- @overriding_map&.lookup(key_sequence) ||
130
- Buffer.current&.keymap&.lookup(key_sequence) ||
131
- GLOBAL_MAP.lookup(key_sequence)
132
- end
133
-
134
126
  def echo_input
135
127
  if @prefix_arg || !@key_sequence.empty?
136
128
  if !@echo_immediately
@@ -157,5 +149,13 @@ module Textbringer
157
149
  @echo_immediately = false
158
150
  end
159
151
  end
152
+
153
+ private
154
+
155
+ def key_binding(key_sequence)
156
+ @overriding_map&.lookup(key_sequence) ||
157
+ Buffer.current&.keymap&.lookup(key_sequence) ||
158
+ GLOBAL_MAP.lookup(key_sequence)
159
+ end
160
160
  end
161
161
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "curses"
4
+
5
+ module Textbringer
6
+ class Face
7
+ attr_reader :name, :attributes
8
+
9
+ @@face_table = {}
10
+ @@next_color_pair = 1
11
+
12
+ def self.[](name)
13
+ @@face_table[name]
14
+ end
15
+
16
+ def self.define(name, **opts)
17
+ if @@face_table.key?(name)
18
+ @@face_table[name].update(**opts)
19
+ else
20
+ @@face_table[name] = new(name, **opts)
21
+ end
22
+ end
23
+
24
+ def self.delete(name)
25
+ @@face_table.delete(name)
26
+ end
27
+
28
+ def initialize(name, **opts)
29
+ @name = name
30
+ @color_pair = @@next_color_pair
31
+ @@next_color_pair += 1
32
+ update(**opts)
33
+ end
34
+
35
+ def update(foreground: -1, background: -1,
36
+ bold: false, underline: false, reverse: false)
37
+ @foreground = foreground
38
+ @background = background
39
+ @bold = bold
40
+ @underline = underline
41
+ @reverse = reverse
42
+ Curses.init_pair(@color_pair,
43
+ Color[foreground], Color[background])
44
+ @attributes = 0
45
+ @attributes |= Curses.color_pair(@color_pair)
46
+ @attributes |= Curses::A_BOLD if bold
47
+ @attributes |= Curses::A_UNDERLINE if underline
48
+ @attributes |= Curses::A_REVERSE if reverse
49
+ self
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Textbringer
4
+ Face.define :mode_line, reverse: true
5
+ Face.define :link, foreground: "blue", bold: true
6
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Textbringer
4
+ Face.define :comment, foreground: "yellow"
5
+ Face.define :preprocessing_directive, foreground: "green"
6
+ Face.define :keyword, foreground: "magenta", bold: true
7
+ Face.define :string, foreground: "blue", bold: true
8
+ end
@@ -96,7 +96,8 @@ module Textbringer
96
96
  GLOBAL_MAP.define_key(?\C-_, :undo)
97
97
  GLOBAL_MAP.define_key("\C-x\C-_", :redo)
98
98
  GLOBAL_MAP.define_key("\C-t", :transpose_chars)
99
- GLOBAL_MAP.define_key(?\n, :newline)
99
+ GLOBAL_MAP.define_key("\C-j", :newline)
100
+ GLOBAL_MAP.define_key("\C-m", :newline)
100
101
  GLOBAL_MAP.define_key("\C-l", :recenter)
101
102
  GLOBAL_MAP.define_key("\C-v", :scroll_up)
102
103
  GLOBAL_MAP.define_key(:npage, :scroll_up)
@@ -106,6 +107,7 @@ module Textbringer
106
107
  GLOBAL_MAP.define_key("\C-x1", :delete_other_windows)
107
108
  GLOBAL_MAP.define_key("\C-x2", :split_window)
108
109
  GLOBAL_MAP.define_key("\C-xo", :other_window)
110
+ GLOBAL_MAP.define_key("\C-x^", :enlarge_window)
109
111
  GLOBAL_MAP.define_key("\C-x\C-c", :exit_textbringer)
110
112
  GLOBAL_MAP.define_key("\C-z", :suspend_textbringer)
111
113
  GLOBAL_MAP.define_key("\C-x\C-f", :find_file)
@@ -113,8 +115,8 @@ module Textbringer
113
115
  GLOBAL_MAP.define_key("\C-x\C-s", :save_buffer)
114
116
  GLOBAL_MAP.define_key("\C-x\C-w", :write_file)
115
117
  GLOBAL_MAP.define_key("\C-xk", :kill_buffer)
116
- GLOBAL_MAP.define_key("\C-x\nf", :set_buffer_file_encoding)
117
- GLOBAL_MAP.define_key("\C-x\nn", :set_buffer_file_format)
118
+ GLOBAL_MAP.define_key("\C-x\C-mf", :set_buffer_file_encoding)
119
+ GLOBAL_MAP.define_key("\C-x\C-mn", :set_buffer_file_format)
118
120
  GLOBAL_MAP.define_key("\ex", :execute_command)
119
121
  GLOBAL_MAP.define_key("\e:", :eval_expression)
120
122
  GLOBAL_MAP.define_key(?\C-u, :universal_argument)
@@ -132,7 +134,8 @@ module Textbringer
132
134
  end
133
135
 
134
136
  MINIBUFFER_LOCAL_MAP = Keymap.new
135
- MINIBUFFER_LOCAL_MAP.define_key(?\n, :exit_recursive_edit)
137
+ MINIBUFFER_LOCAL_MAP.define_key("\C-j", :exit_recursive_edit)
138
+ MINIBUFFER_LOCAL_MAP.define_key("\C-m", :exit_recursive_edit)
136
139
  MINIBUFFER_LOCAL_MAP.define_key(?\t, :complete_minibuffer)
137
140
  MINIBUFFER_LOCAL_MAP.define_key(?\C-g, :abort_recursive_edit)
138
141
  end
@@ -17,6 +17,7 @@ module Textbringer
17
17
  attr_accessor :hook_name
18
18
  attr_accessor :file_name_pattern
19
19
  attr_accessor :interpreter_name_pattern
20
+ attr_reader :syntax_table
20
21
  end
21
22
 
22
23
  def self.define_generic_command(name)
@@ -35,6 +36,10 @@ module Textbringer
35
36
  end
36
37
  end
37
38
 
39
+ def self.define_syntax(face_name, re)
40
+ @syntax_table[face_name] = re
41
+ end
42
+
38
43
  def self.inherited(child)
39
44
  base_name = child.name.slice(/[^:]*\z/)
40
45
  child.mode_name = base_name.sub(/Mode\z/, "")
@@ -50,6 +55,7 @@ module Textbringer
50
55
  Buffer.current.apply_mode(child)
51
56
  end
52
57
  @@mode_list.push(child)
58
+ child.instance_variable_set(:@syntax_table, {})
53
59
  end
54
60
 
55
61
  attr_reader :buffer
@@ -61,5 +67,9 @@ module Textbringer
61
67
  def name
62
68
  self.class.mode_name
63
69
  end
70
+
71
+ def syntax_table
72
+ self.class.syntax_table
73
+ end
64
74
  end
65
75
  end
@@ -5,7 +5,9 @@ module Textbringer
5
5
  define_generic_command :jump_to_source_location
6
6
 
7
7
  BACKTRACE_MODE_MAP = Keymap.new
8
- BACKTRACE_MODE_MAP.define_key("\n", :jump_to_source_location_command)
8
+ BACKTRACE_MODE_MAP.define_key("\C-m", :jump_to_source_location_command)
9
+
10
+ define_syntax :link, /^\S*?:\d+:(?:\d+:)?/
9
11
 
10
12
  def initialize(buffer)
11
13
  super(buffer)
@@ -13,10 +15,11 @@ module Textbringer
13
15
  end
14
16
 
15
17
  def jump_to_source_location
16
- file_name, line_number = get_source_location
18
+ file_name, line_number, column_number = get_source_location
17
19
  if file_name
18
20
  find_file(file_name)
19
21
  goto_line(line_number)
22
+ forward_char(column_number - 1)
20
23
  end
21
24
  end
22
25
 
@@ -25,10 +28,11 @@ module Textbringer
25
28
  def get_source_location
26
29
  @buffer.save_excursion do
27
30
  @buffer.beginning_of_line
28
- if @buffer.looking_at?(/^(\S*?):(\d+):/)
31
+ if @buffer.looking_at?(/^(\S*?):(\d+):(?:(\d+):)?/)
29
32
  file_name = @buffer.match_string(1)
30
33
  line_number = @buffer.match_string(2).to_i
31
- [file_name, line_number]
34
+ column_number = (@buffer.match_string(3) || 1).to_i
35
+ [file_name, line_number, column_number]
32
36
  else
33
37
  nil
34
38
  end
@@ -10,6 +10,32 @@ module Textbringer
10
10
  class CMode < ProgrammingMode
11
11
  self.file_name_pattern = /\A.*\.[ch]\z/i
12
12
 
13
+ KEYWORDS = %w(
14
+ auto break case char const continue default double do
15
+ else enum extern float for goto if inline int long
16
+ register restrict return short signed sizeof static struct
17
+ switch typedef union unsigned void volatile while _Bool
18
+ _Complex _Imaginary
19
+ )
20
+
21
+ define_syntax :comment, /
22
+ (?: \/\* (?> (?:.|\n)*? \*\/ ) ) |
23
+ (?: \/\/ .*(?:\\\n.*)*(?:\z|(?<!\\)\n) )
24
+ /x
25
+
26
+ define_syntax :preprocessing_directive, /
27
+ ^ [\ \t]* (?: \# | %: ) [\ \t]* [_a-zA-Z][_a-zA-Z0-9]*
28
+ /x
29
+
30
+ define_syntax :keyword, /
31
+ \b (?: #{KEYWORDS.join("|")} ) \b
32
+ /x
33
+
34
+ define_syntax :string, /
35
+ (?: " (?: [^\\"] | \\ (?:.|\n) )* " ) |
36
+ (?: ' (?: [^\\'] | \\ (?:.|\n) )* ' )
37
+ /x
38
+
13
39
  def initialize(buffer)
14
40
  super(buffer)
15
41
  @buffer[:indent_level] = CONFIG[:c_indent_level]
@@ -18,7 +44,8 @@ module Textbringer
18
44
 
19
45
  def compile(cmd = read_from_minibuffer("Compile: ",
20
46
  default: default_compile_command))
21
- shell_execute(cmd, "*Compile result*")
47
+ shell_execute(cmd, buffer_name: "*Compile result*",
48
+ mode: BacktraceMode)
22
49
  end
23
50
 
24
51
  def symbol_pattern
@@ -55,13 +82,7 @@ module Textbringer
55
82
  (?<singleline_comment> \/\/ .*? \\\n (?:.|\n)* )
56
83
  ) |
57
84
  (?<keyword>
58
- (?:
59
- auto | break | case | char | const | continue | default | double | do |
60
- else | enum | extern | float | for | goto | if | inline | int | long |
61
- register | restrict | return | short | signed | sizeof | static | struct |
62
- switch | typedef | union | unsigned | void | volatile | while | _Bool |
63
- _Complex | _Imaginary
64
- ) \b
85
+ (?: #{KEYWORDS.join("|")} ) \b
65
86
  ) |
66
87
  (?<constant>
67
88
  (?<floating_constant>
@@ -7,6 +7,8 @@ module Textbringer
7
7
  COMPLETION_LIST_MODE_MAP = Keymap.new
8
8
  COMPLETION_LIST_MODE_MAP.define_key("\n", :choose_completion_command)
9
9
 
10
+ define_syntax :link, /^.+$/
11
+
10
12
  def initialize(buffer)
11
13
  super(buffer)
12
14
  buffer.keymap = COMPLETION_LIST_MODE_MAP
@@ -14,11 +14,11 @@ module Textbringer
14
14
 
15
15
  PROGRAMMING_MODE_MAP = Keymap.new
16
16
  PROGRAMMING_MODE_MAP.define_key("\t", :indent_line_command)
17
- PROGRAMMING_MODE_MAP.define_key("\n", :newline_and_reindent_command)
17
+ PROGRAMMING_MODE_MAP.define_key("\C-m", :newline_and_reindent_command)
18
18
  PROGRAMMING_MODE_MAP.define_key("\C-c\C-n", :forward_definition_command)
19
19
  PROGRAMMING_MODE_MAP.define_key("\C-c\C-p", :backward_definition_command)
20
20
  PROGRAMMING_MODE_MAP.define_key("\C-c\C-c", :compile_command)
21
- PROGRAMMING_MODE_MAP.define_key("\C-ct", :toggle_test_command)
21
+ PROGRAMMING_MODE_MAP.define_key("\C-c\C-t", :toggle_test_command)
22
22
 
23
23
  def initialize(buffer)
24
24
  super(buffer)
@@ -64,7 +64,7 @@ module Textbringer
64
64
  @buffer.save_excursion do
65
65
  pos = @buffer.point
66
66
  @buffer.beginning_of_line
67
- if /\A\s+\z/ =~ @buffer.substring(@buffer.point, pos)
67
+ if /\A[ \t]+\z/ =~ @buffer.substring(@buffer.point, pos)
68
68
  @buffer.delete_region(@buffer.point, pos)
69
69
  n += 1
70
70
  end
@@ -12,6 +12,65 @@ module Textbringer
12
12
  (?:Gem|Rake|Cap|Thor|Vagrant|Guard|Pod)file)\z/ix
13
13
  self.interpreter_name_pattern = /ruby/i
14
14
 
15
+ define_syntax :comment, /
16
+ (?: \#.*(?:\\\n.*)*(?:\z|(?<!\\)\n) ) |
17
+ (?: ^=begin (?:.|\n)* (?> ^=end \b ) )
18
+ /x
19
+
20
+ define_syntax :keyword, /
21
+ (?<![$@.]) \b (?: (?:
22
+ class | module | def | undef | begin | rescue | ensure | end |
23
+ if | unless | then | elsif | else | case | when | while | until |
24
+ for | break | next | redo | retry | in | do | return | yield |
25
+ super | self | nil | true | false | and | or | not | alias
26
+ ) \b | defined\? )
27
+ /x
28
+
29
+ define_syntax :string, /
30
+ (?: (?<! [a-zA-Z] ) \? (:? [^\\\s] | \\ . ) ) |
31
+ (?: %[qQrwWsix]?\{ (?: [^\\}] | \\ . )* \} ) |
32
+ (?: %[qQrwWsix]?\( (?: [^\\)] | \\ . )* \) ) |
33
+ (?: %[qQrwWsix]?\[ (?: [^\\\]] | \\ . )* \] ) |
34
+ (?: %[qQrwWsix]?< (?: [^\\>] | \\ . )* > ) |
35
+ (?:
36
+ %[qQrwWsix]?
37
+ (?<string_delimiter>[^{(\[<a-zA-Z0-9\s\u{0100}-\u{10ffff}])
38
+ (?: (?! \k<string_delimiter> ) [^\\] | \\ . )*
39
+ \k<string_delimiter>
40
+ ) |
41
+ (?:
42
+ (?<! \$ )
43
+ " (?: [^\\"] | \\ . )* "
44
+ ) |
45
+ (?:
46
+ (?<! \$ )
47
+ ' (?: [^\\'] | \\ . )* '
48
+ ) |
49
+ (?:
50
+ (?<! [$.] | def | def \s )
51
+ ` (?: [^\\`] | \\ . )* `
52
+ ) |
53
+ (?:
54
+ (?<=
55
+ ^ |
56
+ \b and | \b or | \b while | \b until | \b unless | \b if |
57
+ \b elsif | \b when | \b not | \b then | \b else |
58
+ [;~=!|&(,\[<>?:*+-]
59
+ ) \s*
60
+ \/ (?: [^\\\/] | \\ . )* \/[iomxneus]*
61
+ ) |
62
+ (?:
63
+ (?<! class | class \s | [\]})"'.] | :: | \w )
64
+ <<-?(?<heredoc_quote>['"`]?)
65
+ (?<heredoc_terminator>
66
+ [_a-zA-Z\u{0100}-\u{10ffff}]
67
+ [_a-zA-Z0-9\u{0100}-\u{10ffff}]*
68
+ )
69
+ \k<heredoc_quote>
70
+ (?> (?:.|\n)*? \k<heredoc_terminator> )
71
+ )
72
+ /x
73
+
15
74
  def initialize(buffer)
16
75
  super(buffer)
17
76
  @buffer[:indent_level] = CONFIG[:ruby_indent_level]
@@ -62,8 +121,8 @@ module Textbringer
62
121
 
63
122
  def compile(cmd = read_from_minibuffer("Compile: ",
64
123
  default: default_compile_command))
65
- shell_execute(cmd, "*Ruby compile result*")
66
- backtrace_mode
124
+ shell_execute(cmd, buffer_name: "*Ruby compile result*",
125
+ mode: BacktraceMode)
67
126
  end
68
127
 
69
128
  def symbol_pattern
@@ -176,7 +235,9 @@ module Textbringer
176
235
  e != :on_sp && e != :on_nl && e != :on_ignored_nl
177
236
  }
178
237
  if (last_event == :on_op && last_text != "|") ||
179
- last_event == :on_period
238
+ last_event == :on_period ||
239
+ (last_event == :on_comma &&
240
+ event != :on_lbrace && event != :on_lbracket)
180
241
  indentation += @buffer[:indent_level]
181
242
  end
182
243
  indentation
@@ -90,6 +90,7 @@ module Textbringer
90
90
  }
91
91
 
92
92
  def read_from_minibuffer(prompt, completion_proc: nil, default: nil,
93
+ initial_value: nil,
93
94
  keymap: MINIBUFFER_LOCAL_MAP)
94
95
  if Window.echo_area.active?
95
96
  raise EditorError,
@@ -104,9 +105,9 @@ module Textbringer
104
105
  Buffer.minibuffer[:completion_proc] = completion_proc
105
106
  Window.echo_area.active = true
106
107
  begin
107
- Buffer.minibuffer.delete_region(Buffer.minibuffer.point_min,
108
- Buffer.minibuffer.point_max)
109
108
  Window.current = Window.echo_area
109
+ Buffer.minibuffer.clear
110
+ Buffer.minibuffer.insert(initial_value) if initial_value
110
111
  if default
111
112
  prompt = prompt.sub(/:/, " (default #{default}):")
112
113
  end
@@ -275,8 +276,17 @@ module Textbringer
275
276
  RbConfig::CONFIG["ruby_install_name"]
276
277
  end
277
278
 
278
- def insert(s)
279
- Buffer.current.insert(s)
279
+ [
280
+ :beginning_of_buffer?,
281
+ :end_of_buffer?,
282
+ :beginning_of_line?,
283
+ :end_of_line?,
284
+ :insert,
285
+ :gsub
286
+ ].each do |name|
287
+ define_method(name) do |*args, &block|
288
+ Buffer.current.send(name, *args, &block)
289
+ end
280
290
  end
281
291
  end
282
292
  end
@@ -1,3 +1,3 @@
1
1
  module Textbringer
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.8"
3
3
  end
@@ -48,9 +48,11 @@ module Textbringer
48
48
  name.slice(/\AKEY_(.*)/, 1).downcase.intern
49
49
  end
50
50
 
51
+ @@started = false
51
52
  @@windows = []
52
53
  @@current = nil
53
54
  @@echo_area = nil
55
+ @@has_colors = false
54
56
 
55
57
  def self.windows
56
58
  @@windows
@@ -119,10 +121,42 @@ module Textbringer
119
121
  @@echo_area
120
122
  end
121
123
 
124
+ def self.has_colors=(value)
125
+ @@has_colors = value
126
+ end
127
+
128
+ def self.has_colors?
129
+ @@has_colors
130
+ end
131
+
132
+ def self.colors
133
+ Curses.colors
134
+ end
135
+
136
+ def self.set_default_colors(fg, bg)
137
+ Curses.assume_default_colors(Color[fg], Color[bg])
138
+ Window.redraw
139
+ end
140
+
141
+ def self.load_faces
142
+ require_relative "faces/basic"
143
+ require_relative "faces/programming"
144
+ end
145
+
122
146
  def self.start
147
+ if @@started
148
+ raise EditorError, "Already started"
149
+ end
123
150
  Curses.init_screen
124
151
  Curses.noecho
125
152
  Curses.raw
153
+ Curses.nonl
154
+ self.has_colors = Curses.has_colors?
155
+ if has_colors?
156
+ Curses.start_color
157
+ Curses.use_default_colors
158
+ load_faces
159
+ end
126
160
  begin
127
161
  window =
128
162
  Textbringer::Window.new(Window.lines - 1, Window.columns, 0, 0)
@@ -134,11 +168,18 @@ module Textbringer
134
168
  Buffer.minibuffer.keymap = MINIBUFFER_LOCAL_MAP
135
169
  @@echo_area.buffer = Buffer.minibuffer
136
170
  @@windows.push(@@echo_area)
171
+ @@started = true
137
172
  yield
138
173
  ensure
174
+ @@windows.each do |win|
175
+ win.delete
176
+ end
177
+ @@windows.clear
139
178
  Curses.echo
140
179
  Curses.noraw
180
+ Curses.nl
141
181
  Curses.close_screen
182
+ @@started = false
142
183
  end
143
184
  end
144
185
 
@@ -173,7 +214,8 @@ module Textbringer
173
214
 
174
215
  def self.resize
175
216
  @@windows.delete_if do |window|
176
- if !window.echo_area? && window.y > Window.lines - 4
217
+ if !window.echo_area? &&
218
+ window.y > Window.lines - CONFIG[:window_min_height]
177
219
  window.delete
178
220
  true
179
221
  else
@@ -284,7 +326,7 @@ module Textbringer
284
326
  end
285
327
  KEY_NAMES[key] || key
286
328
  else
287
- key&.encode(Encoding::UTF_8)&.tr("\r", "\n")
329
+ key&.encode(Encoding::UTF_8)
288
330
  end
289
331
  end
290
332
 
@@ -329,6 +371,40 @@ module Textbringer
329
371
  end
330
372
  end
331
373
 
374
+ def highlight
375
+ @highlight_on = {}
376
+ @highlight_off = {}
377
+ return if !@@has_colors || !CONFIG[:syntax_highlight]
378
+ syntax_table = @buffer.mode.syntax_table
379
+ return if syntax_table.empty?
380
+ if @buffer.bytesize < CONFIG[:highlight_buffer_size_limit]
381
+ base_pos = @buffer.point_min
382
+ s = @buffer.to_s
383
+ else
384
+ base_pos = @buffer.point
385
+ len = columns * (lines - 1) / 2 * 3
386
+ s = @buffer.substring(@buffer.point, @buffer.point + len).scrub("")
387
+ end
388
+ re_str = syntax_table.map { |name, re|
389
+ "(?<#{name}>#{re})"
390
+ }.join("|")
391
+ re = Regexp.new(re_str)
392
+ names = syntax_table.keys
393
+ s.scan(re) do
394
+ b = base_pos + $`.bytesize
395
+ e = b + $&.bytesize
396
+ if b < @buffer.point && @buffer.point < e
397
+ b = @buffer.point
398
+ end
399
+ name = names.find { |n| $~[n] }
400
+ attributes = Face[name]&.attributes
401
+ if attributes
402
+ @highlight_on[b] = attributes
403
+ @highlight_off[e] = attributes
404
+ end
405
+ end
406
+ end
407
+
332
408
  def redisplay
333
409
  return if @buffer.nil?
334
410
  redisplay_mode_line
@@ -342,8 +418,10 @@ module Textbringer
342
418
  framer
343
419
  y = x = 0
344
420
  @buffer.point_to_mark(@top_of_window)
421
+ highlight
345
422
  @window.erase
346
423
  @window.setpos(0, 0)
424
+ @window.attrset(0)
347
425
  if current? && @buffer.visible_mark &&
348
426
  @buffer.point_after_mark?(@buffer.visible_mark)
349
427
  @window.attron(Curses::A_REVERSE)
@@ -368,6 +446,12 @@ module Textbringer
368
446
  @window.attron(Curses::A_REVERSE)
369
447
  end
370
448
  end
449
+ if attrs = @highlight_off[@buffer.point]
450
+ @window.attroff(attrs)
451
+ end
452
+ if attrs = @highlight_on[@buffer.point]
453
+ @window.attron(attrs)
454
+ end
371
455
  c = @buffer.char_after
372
456
  if c == "\n"
373
457
  @window.clrtoeol
@@ -478,11 +562,11 @@ module Textbringer
478
562
  end
479
563
 
480
564
  def split
481
- if lines < 6
482
- raise EditorError, "Window too small"
483
- end
484
565
  old_lines = lines
485
566
  new_lines = (old_lines / 2.0).ceil
567
+ if new_lines < CONFIG[:window_min_height]
568
+ raise EditorError, "Window too small"
569
+ end
486
570
  resize(new_lines, columns)
487
571
  new_window = Window.new(old_lines - new_lines, columns, y + new_lines, x)
488
572
  new_window.buffer = buffer
@@ -490,6 +574,47 @@ module Textbringer
490
574
  @@windows.insert(i + 1, new_window)
491
575
  end
492
576
 
577
+ def enlarge(n)
578
+ if n > 0
579
+ max_height = Window.lines -
580
+ CONFIG[:window_min_height] * (@@windows.size - 2) - 1
581
+ new_lines = [lines + n, max_height].min
582
+ needed_lines = new_lines - lines
583
+ resize(new_lines, columns)
584
+ i = @@windows.index(self)
585
+ indices = (i + 1).upto(@@windows.size - 2).to_a +
586
+ (i - 1).downto(0).to_a
587
+ indices.each do |j|
588
+ break if needed_lines == 0
589
+ window = @@windows[j]
590
+ extended_lines = [
591
+ window.lines - CONFIG[:window_min_height],
592
+ needed_lines
593
+ ].min
594
+ window.resize(window.lines - extended_lines, window.columns)
595
+ needed_lines -= extended_lines
596
+ end
597
+ y = 0
598
+ @@windows.each do |win|
599
+ win.move(y, win.x)
600
+ y += win.lines
601
+ end
602
+ elsif n < 0 && @@windows.size > 2
603
+ new_lines = [lines + n, CONFIG[:window_min_height]].max
604
+ diff = lines - new_lines
605
+ resize(new_lines, columns)
606
+ i = @@windows.index(self)
607
+ if i < @@windows.size - 2
608
+ window = @@windows[i + 1]
609
+ window.move(window.y - diff, window.x)
610
+ else
611
+ window = @@windows[i - 1]
612
+ move(self.y + diff, self.x)
613
+ end
614
+ window.resize(window.lines + diff, window.columns)
615
+ end
616
+ end
617
+
493
618
  private
494
619
 
495
620
  def initialize_window(num_lines, num_columns, y, x)
@@ -522,7 +647,8 @@ module Textbringer
522
647
  def redisplay_mode_line
523
648
  @mode_line.erase
524
649
  @mode_line.setpos(0, 0)
525
- @mode_line.attron(Curses::A_REVERSE)
650
+ attrs = @@has_colors ? Face[:mode_line].attributes : Curses::A_REVERSE
651
+ @mode_line.attrset(attrs)
526
652
  @mode_line.addstr("#{@buffer.name} ")
527
653
  @mode_line.addstr("[+]") if @buffer.modified?
528
654
  @mode_line.addstr("[RO]") if @buffer.read_only?
@@ -540,7 +666,7 @@ module Textbringer
540
666
  @mode_line.addstr(" #{line},#{column}")
541
667
  @mode_line.addstr(" (#{@buffer.mode&.name || 'None'})")
542
668
  @mode_line.addstr(" " * (columns - @mode_line.curx))
543
- @mode_line.attroff(Curses::A_REVERSE)
669
+ @mode_line.attrset(0)
544
670
  @mode_line.noutrefresh
545
671
  end
546
672
 
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
21
21
 
22
22
  spec.required_ruby_version = '>= 2.3'
23
23
 
24
- spec.add_runtime_dependency "curses", "~> 1.1"
24
+ spec.add_runtime_dependency "curses", "~> 1.2"
25
25
  spec.add_runtime_dependency "unicode-display_width", "~> 1.1"
26
26
  spec.add_runtime_dependency "clipboard", "~> 1.1"
27
27
  spec.add_runtime_dependency "ffi"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: textbringer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-02-12 00:00:00.000000000 Z
11
+ date: 2017-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: curses
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '1.1'
19
+ version: '1.2'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '1.1'
26
+ version: '1.2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: unicode-display_width
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -229,6 +229,7 @@ files:
229
229
  - exe/textbringer
230
230
  - lib/textbringer.rb
231
231
  - lib/textbringer/buffer.rb
232
+ - lib/textbringer/color.rb
232
233
  - lib/textbringer/commands.rb
233
234
  - lib/textbringer/commands/buffers.rb
234
235
  - lib/textbringer/commands/clipboard.rb
@@ -242,6 +243,9 @@ files:
242
243
  - lib/textbringer/config.rb
243
244
  - lib/textbringer/controller.rb
244
245
  - lib/textbringer/errors.rb
246
+ - lib/textbringer/face.rb
247
+ - lib/textbringer/faces/basic.rb
248
+ - lib/textbringer/faces/programming.rb
245
249
  - lib/textbringer/keymap.rb
246
250
  - lib/textbringer/mode.rb
247
251
  - lib/textbringer/modes/backtrace_mode.rb