textbringer 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Textbringer
4
+ module Commands
5
+ [
6
+ :forward_char,
7
+ :backward_char,
8
+ :forward_word,
9
+ :backward_word,
10
+ :next_line,
11
+ :previous_line,
12
+ :delete_char,
13
+ :backward_delete_char,
14
+ ].each do |name|
15
+ define_command(name) do |n = number_prefix_arg|
16
+ Buffer.current.send(name, n)
17
+ end
18
+ end
19
+
20
+ [
21
+ :beginning_of_line,
22
+ :end_of_line,
23
+ :beginning_of_buffer,
24
+ :end_of_buffer,
25
+ :exchange_point_and_mark,
26
+ :copy_region,
27
+ :kill_region,
28
+ :yank,
29
+ :newline,
30
+ :delete_region,
31
+ :transpose_chars
32
+ ].each do |name|
33
+ define_command(name) do
34
+ Buffer.current.send(name)
35
+ end
36
+ end
37
+
38
+ define_command(:set_mark_command) do
39
+ Buffer.current.set_mark
40
+ message("Mark set")
41
+ end
42
+
43
+ define_command(:goto_char) do
44
+ |n = read_from_minibuffer("Go to char: ")|
45
+ Buffer.current.goto_char(n.to_i)
46
+ Window.current.recenter_if_needed
47
+ end
48
+
49
+ define_command(:goto_line) do
50
+ |n = read_from_minibuffer("Go to line: ")|
51
+ Buffer.current.goto_line(n.to_i)
52
+ Window.current.recenter_if_needed
53
+ end
54
+
55
+ define_command(:self_insert) do |n = number_prefix_arg|
56
+ c = Controller.current.last_key
57
+ merge_undo = Controller.current.last_command == :self_insert
58
+ n.times do
59
+ Buffer.current.insert(c, merge_undo)
60
+ end
61
+ end
62
+
63
+ define_command(:quoted_insert) do |n = number_prefix_arg|
64
+ c = Controller.current.read_char
65
+ if !c.is_a?(String)
66
+ raise EditorError, "Invalid key"
67
+ end
68
+ n.times do
69
+ Buffer.current.insert(c)
70
+ end
71
+ end
72
+
73
+ define_command(:kill_line) do
74
+ Buffer.current.kill_line(Controller.current.last_command == :kill_region)
75
+ Controller.current.this_command = :kill_region
76
+ end
77
+
78
+ define_command(:kill_word) do
79
+ Buffer.current.kill_word(Controller.current.last_command == :kill_region)
80
+ Controller.current.this_command = :kill_region
81
+ end
82
+
83
+ define_command(:yank_pop) do
84
+ if Controller.current.last_command != :yank
85
+ raise EditorError, "Previous command was not a yank"
86
+ end
87
+ Buffer.current.yank_pop
88
+ Controller.current.this_command = :yank
89
+ end
90
+
91
+ define_command(:undo) do
92
+ Buffer.current.undo
93
+ message("Undo!")
94
+ end
95
+
96
+ define_command(:redo) do
97
+ Buffer.current.redo
98
+ message("Redo!")
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "clipboard"
4
+
5
+ module Textbringer
6
+ module Commands
7
+ CLIPBOARD_AVAILABLE =
8
+ Clipboard.implementation.name != :Linux || ENV["DISPLAY"]
9
+
10
+ if CLIPBOARD_AVAILABLE
11
+ GLOBAL_MAP.define_key("\ew", :clipboard_copy_region)
12
+ GLOBAL_MAP.define_key("\C-w", :clipboard_kill_region)
13
+ GLOBAL_MAP.define_key("\C-y", :clipboard_yank)
14
+ end
15
+
16
+ define_command(:clipboard_copy_region) do
17
+ copy_region
18
+ Clipboard.copy(KILL_RING.current)
19
+ end
20
+
21
+ define_command(:clipboard_kill_region) do
22
+ kill_region
23
+ Clipboard.copy(KILL_RING.current)
24
+ end
25
+
26
+ define_command(:clipboard_yank) do
27
+ s = Clipboard.paste.encode(Encoding::UTF_8)
28
+ if KILL_RING.empty? || KILL_RING.current != s
29
+ KILL_RING.push(s)
30
+ end
31
+ yank
32
+ Controller.current.this_command = :yank
33
+ end
34
+ end
35
+ end
@@ -50,24 +50,20 @@ module Textbringer
50
50
  CTAGS[:candidates] = candidates
51
51
  index = 0
52
52
  end
53
- tag_mark_stack = CTAGS[:tag_mark_stack]
54
- if tag_mark_stack.size == TAG_MARK_LIMIT
55
- mark = tag_mark_stack.shift
56
- mark.delete
57
- end
58
- tag_mark_stack.push(Buffer.current.new_mark)
59
53
  file, addr, n = candidates[index]
60
- find_file(file)
61
54
  case addr
62
55
  when /\A\d+\z/
56
+ push_tag_mark_and_find_file(file)
63
57
  goto_line(addr.to_i)
64
58
  when %r'\A/\^(.*)\$/\z'
59
+ push_tag_mark_and_find_file(file)
65
60
  beginning_of_buffer
66
61
  n.times do
67
62
  re_search_forward("^" + Regexp.quote($1) + "$")
68
63
  end
69
64
  beginning_of_line
70
65
  when %r'\A\?\^(.*)\$\?\z'
66
+ push_tag_mark_and_find_file(file)
71
67
  end_of_buffer
72
68
  n.times do
73
69
  re_search_backward("^" + Regexp.quote($1) + "$")
@@ -110,5 +106,16 @@ module Textbringer
110
106
  mark.delete
111
107
  end
112
108
  end
109
+
110
+ private
111
+
112
+ def push_tag_mark_and_find_file(file)
113
+ tag_mark_stack = CTAGS[:tag_mark_stack]
114
+ if tag_mark_stack.size == TAG_MARK_LIMIT
115
+ tag_mark_stack.shift.delete
116
+ end
117
+ tag_mark_stack.push(Buffer.current.new_mark)
118
+ find_file(file)
119
+ end
113
120
  end
114
121
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Textbringer
4
- using Module.new {
4
+ module DabbrevExtension
5
5
  refine Buffer do
6
- def dabbrev_expand(contd)
6
+ def dabbrev_expand(contd = false)
7
7
  if contd && self[:dabbrev_stem]
8
8
  buffers = self[:dabbrev_buffers]
9
9
  buffer = self[:dabbrev_buffer]
@@ -13,43 +13,26 @@ module Textbringer
13
13
  candidates = self[:dabbrev_candidates]
14
14
  else
15
15
  buffers = Buffer.to_a
16
- buffer = buffers.pop
17
- stem = save_excursion {
18
- pos = point
19
- backward_word(regexp: /[\p{Letter}\p{Number}_\-]/)
20
- substring(point, pos)
21
- }
22
- if stem.empty?
23
- raise "No possible abbreviation"
24
- end
16
+ buffers.delete(self)
17
+ buffer = self
18
+ stem = get_stem_at_point
25
19
  pos = point
26
20
  direction = :backward
27
21
  candidates = []
28
22
  end
29
- candidates_exclusion = candidates.empty? ? "" :
30
- "(?!(?:" + candidates.map { |s|
31
- Regexp.quote(s)
32
- }.join("|") + ")\\b)"
33
- re = /\b#{Regexp.quote(stem)}#{candidates_exclusion}
34
- ([\p{Letter}\p{Number}_\-]+)/x
23
+ re = dabbrev_regexp(stem, candidates)
35
24
  candidate = nil
36
25
  loop do
37
26
  pos, candidate = buffer.dabbrev_search(re, pos, direction)
38
- if pos
39
- break
27
+ break if pos
28
+ if direction == :backward
29
+ pos = buffer.point
30
+ direction = :forward
40
31
  else
41
- if direction == :backward
42
- pos = buffer.point
43
- direction = :forward
44
- else
45
- buffer = buffers.pop
46
- if buffer
47
- pos = buffer.point
48
- direction = :backward
49
- else
50
- break
51
- end
52
- end
32
+ buffer = buffers.pop
33
+ break if buffer.nil?
34
+ pos = buffer.point
35
+ direction = :backward
53
36
  end
54
37
  end
55
38
  if !candidates.empty?
@@ -82,8 +65,32 @@ module Textbringer
82
65
  end
83
66
  end
84
67
  end
68
+
69
+ private
70
+
71
+ def get_stem_at_point
72
+ save_excursion {
73
+ pos = point
74
+ backward_word(regexp: /[\p{Letter}\p{Number}_\-]/)
75
+ if point == pos
76
+ raise EditorError, "No possible abbreviation"
77
+ end
78
+ substring(point, pos)
79
+ }
80
+ end
81
+
82
+ def dabbrev_regexp(stem, candidates)
83
+ candidates_exclusion = candidates.empty? ? "" :
84
+ "(?!(?:" + candidates.map { |s|
85
+ Regexp.quote(s)
86
+ }.join("|") + ")\\b)"
87
+ /\b#{Regexp.quote(stem)}#{candidates_exclusion}
88
+ ([\p{Letter}\p{Number}_\-]+)/x
89
+ end
85
90
  end
86
- }
91
+ end
92
+
93
+ using DabbrevExtension
87
94
 
88
95
  module Commands
89
96
  GLOBAL_MAP.define_key("\e/", :dabbrev_expand_command)
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Textbringer
4
+ module Commands
5
+ define_command(:find_file) do
6
+ |file_name = read_file_name("Find file: ")|
7
+ buffer = Buffer.find_file(file_name)
8
+ if buffer.new_file?
9
+ message("New file")
10
+ end
11
+ switch_to_buffer(buffer)
12
+ shebang = buffer.save_excursion {
13
+ buffer.beginning_of_buffer
14
+ buffer.looking_at?(/#!.*$/) ? buffer.match_string(0) : nil
15
+ }
16
+ mode = Mode.list.find { |m|
17
+ (m.file_name_pattern &&
18
+ m.file_name_pattern =~ File.basename(buffer.file_name)) ||
19
+ (m.interpreter_name_pattern &&
20
+ m.interpreter_name_pattern =~ shebang)
21
+ } || FundamentalMode
22
+ send(mode.command_name)
23
+ end
24
+
25
+ define_command(:save_buffer) do
26
+ if Buffer.current.file_name.nil?
27
+ Buffer.current.file_name = read_file_name("File to save in: ")
28
+ next if Buffer.current.file_name.nil?
29
+ end
30
+ if Buffer.current.file_modified?
31
+ unless yes_or_no?("File changed on disk. Save anyway?")
32
+ message("Cancelled")
33
+ next
34
+ end
35
+ end
36
+ Buffer.current.save
37
+ message("Wrote #{Buffer.current.file_name}")
38
+ end
39
+
40
+ define_command(:write_file) do
41
+ |file_name = read_file_name("Write file: ")|
42
+ if File.directory?(file_name)
43
+ file_name = File.expand_path(Buffer.current.name, file_name)
44
+ end
45
+ if File.exist?(file_name)
46
+ unless y_or_n?("File `#{file_name}' exists; overwrite?")
47
+ message("Cancelled")
48
+ next
49
+ end
50
+ end
51
+ Buffer.current.save(file_name)
52
+ message("Wrote #{Buffer.current.file_name}")
53
+ end
54
+
55
+ define_command(:set_buffer_file_encoding) do
56
+ |enc = read_from_minibuffer("File encoding: ",
57
+ default: Buffer.current.file_encoding.name)|
58
+ Buffer.current.file_encoding = enc
59
+ end
60
+
61
+ define_command(:set_buffer_file_format) do
62
+ |format = read_from_minibuffer("File format: ",
63
+ default: Buffer.current.file_format.to_s)|
64
+ Buffer.current.file_format = format
65
+ end
66
+
67
+ define_command(:pwd) do
68
+ message(Dir.pwd)
69
+ end
70
+
71
+ define_command(:chdir) do
72
+ |dir_name = read_file_name("Change directory: ")|
73
+ Dir.chdir(dir_name)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Textbringer
4
+ module Commands
5
+ ISEARCH_MODE_MAP = Keymap.new
6
+ (?\x20..?\x7e).each do |c|
7
+ ISEARCH_MODE_MAP.define_key(c, :isearch_printing_char)
8
+ end
9
+ ISEARCH_MODE_MAP.define_key(?\t, :isearch_printing_char)
10
+ ISEARCH_MODE_MAP.handle_undefined_key do |key|
11
+ if key.is_a?(String) && /[\0-\x7f]/ !~ key
12
+ :isearch_printing_char
13
+ else
14
+ nil
15
+ end
16
+ end
17
+ ISEARCH_MODE_MAP.define_key(:backspace, :isearch_delete_char)
18
+ ISEARCH_MODE_MAP.define_key(?\C-h, :isearch_delete_char)
19
+ ISEARCH_MODE_MAP.define_key(?\C-s, :isearch_repeat_forward)
20
+ ISEARCH_MODE_MAP.define_key(?\C-r, :isearch_repeat_backward)
21
+ ISEARCH_MODE_MAP.define_key(?\n, :isearch_exit)
22
+ ISEARCH_MODE_MAP.define_key(?\C-g, :isearch_abort)
23
+
24
+ ISEARCH_STATUS = {
25
+ forward: true,
26
+ string: "",
27
+ last_string: "",
28
+ start: 0,
29
+ last_pos: 0,
30
+ recursive_edit: false
31
+ }
32
+
33
+ define_command(:isearch_forward) do |**options|
34
+ isearch_mode(true, **options)
35
+ end
36
+
37
+ define_command(:isearch_backward) do |**options|
38
+ isearch_mode(false, **options)
39
+ end
40
+
41
+ def isearch_mode(forward, recursive_edit: false)
42
+ ISEARCH_STATUS[:forward] = forward
43
+ ISEARCH_STATUS[:string] = String.new
44
+ ISEARCH_STATUS[:recursive_edit] = recursive_edit
45
+ Controller.current.overriding_map = ISEARCH_MODE_MAP
46
+ run_hooks(:isearch_mode_hook)
47
+ add_hook(:pre_command_hook, :isearch_pre_command_hook)
48
+ ISEARCH_STATUS[:start] = ISEARCH_STATUS[:last_pos] = Buffer.current.point
49
+ if Buffer.current != Buffer.minibuffer
50
+ message(isearch_prompt, log: false)
51
+ end
52
+ if recursive_edit
53
+ recursive_edit()
54
+ end
55
+ end
56
+
57
+ def isearch_prompt
58
+ if ISEARCH_STATUS[:forward]
59
+ "I-search: "
60
+ else
61
+ "I-search backward: "
62
+ end
63
+ end
64
+
65
+ def isearch_pre_command_hook
66
+ if /\Aisearch_/ !~ Controller.current.this_command
67
+ isearch_done
68
+ end
69
+ end
70
+
71
+ def isearch_done
72
+ Buffer.current.delete_visible_mark
73
+ Controller.current.overriding_map = nil
74
+ remove_hook(:pre_command_hook, :isearch_pre_command_hook)
75
+ ISEARCH_STATUS[:last_string] = ISEARCH_STATUS[:string]
76
+ if ISEARCH_STATUS[:recursive_edit]
77
+ exit_recursive_edit
78
+ end
79
+ end
80
+
81
+ define_command(:isearch_exit) do
82
+ isearch_done
83
+ end
84
+
85
+ define_command(:isearch_abort) do
86
+ goto_char(Buffer.current[:isearch_start])
87
+ isearch_done
88
+ raise Quit
89
+ end
90
+
91
+ define_command(:isearch_printing_char) do
92
+ c = Controller.current.last_key
93
+ ISEARCH_STATUS[:string].concat(c)
94
+ isearch_search
95
+ end
96
+
97
+ define_command(:isearch_delete_char) do
98
+ ISEARCH_STATUS[:string].chop!
99
+ isearch_search
100
+ end
101
+
102
+ def isearch_search
103
+ forward = ISEARCH_STATUS[:forward]
104
+ options = if /\A[A-Z]/ =~ ISEARCH_STATUS[:string]
105
+ nil
106
+ else
107
+ Regexp::IGNORECASE
108
+ end
109
+ re = Regexp.new(Regexp.quote(ISEARCH_STATUS[:string]), options)
110
+ last_pos = ISEARCH_STATUS[:last_pos]
111
+ offset = forward ? last_pos : last_pos - ISEARCH_STATUS[:string].bytesize
112
+ if Buffer.current.byteindex(forward, re, offset)
113
+ if Buffer.current != Buffer.minibuffer
114
+ message(isearch_prompt + ISEARCH_STATUS[:string], log: false)
115
+ end
116
+ Buffer.current.set_visible_mark(forward ? match_beginning(0) :
117
+ match_end(0))
118
+ goto_char(forward ? match_end(0) : match_beginning(0))
119
+ else
120
+ if Buffer.current != Buffer.minibuffer
121
+ message("Failing " + isearch_prompt + ISEARCH_STATUS[:string],
122
+ log: false)
123
+ end
124
+ end
125
+ end
126
+
127
+ def isearch_repeat_forward
128
+ isearch_repeat(true)
129
+ end
130
+
131
+ def isearch_repeat_backward
132
+ isearch_repeat(false)
133
+ end
134
+
135
+ def isearch_repeat(forward)
136
+ ISEARCH_STATUS[:forward] = forward
137
+ ISEARCH_STATUS[:last_pos] = Buffer.current.point
138
+ if ISEARCH_STATUS[:string].empty?
139
+ ISEARCH_STATUS[:string] = ISEARCH_STATUS[:last_string]
140
+ end
141
+ isearch_search
142
+ end
143
+ end
144
+ end