textbringer 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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