giga 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +1 -0
  4. data/bin/giga +5 -0
  5. data/lib/giga/version.rb +3 -0
  6. data/lib/giga.rb +181 -0
  7. metadata +62 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 206803871e1829a4cbfe9cedf95d4ecd4c799bc5c5c47e9eda33289d2eade515
4
+ data.tar.gz: '03829d2857eeed92b9bbafcc6cc39d0a935e23f33abc9beb020d006d534e8eb4'
5
+ SHA512:
6
+ metadata.gz: 96efc65d1868d5739618738d4e10e6822e1c9890025d3b793a68f49ff7bf0702927f5d88f503e02aa0152ddef708c4b1a4a1d7f79d408984a6483c152290e724
7
+ data.tar.gz: e15600e66dbc3aa2cf2f5f9eaa5370a68de626b387adaaeb99e885fc87c8e42da866f1cec1cd9a0dcefe0a01bed498a21fe71a760b8297b35fb0818188fc6b66
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Pierre Jambet
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ # Giga the text editor
data/bin/giga ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'giga'
4
+
5
+ Giga::Editor.new.start
@@ -0,0 +1,3 @@
1
+ module Giga
2
+ VERSION = '0.0.0'
3
+ end
data/lib/giga.rb ADDED
@@ -0,0 +1,181 @@
1
+ require 'termios'
2
+
3
+ module Giga
4
+ class Editor
5
+ def initialize
6
+ end
7
+
8
+ def refresh
9
+ append_buffer = ""
10
+ append_buffer << "\x1b[?25l" # Hide cursor
11
+ append_buffer << "\x1b[H"
12
+ # stderr_log @text_content
13
+ @height.times do |row_index|
14
+
15
+ if row_index >= @text_content.count
16
+ append_buffer << "~\x1b[0K\r\n"
17
+ # stderr_log("Row index: #{row_index}")
18
+ # stderr_log("Line count: #{@text_content.count}")
19
+ next
20
+ end
21
+
22
+ row = @text_content[row_index] || ""
23
+ # stderr_log "'#{row}'"
24
+ append_buffer << row
25
+ # https://notes.burke.libbey.me/ansi-escape-codes/
26
+ # https://en.wikipedia.org/wiki/ANSI_escape_code
27
+ append_buffer << "\x1b[39m" # Default foregroung color
28
+ append_buffer << "\x1b[0K" # Erase the rest of the line
29
+ append_buffer << "\r\n"
30
+ end
31
+ append_buffer.strip!
32
+ append_buffer << "\x1b[H"
33
+ x, y = @cursor_position
34
+ # x += 1 if x > 0
35
+ append_buffer << "\x1b[#{y};#{x}H"
36
+ # append_buffer << "\x1b[;1H"
37
+ append_buffer << "\x1b[?25h" # Show cursor
38
+ stderr_log("'#{append_buffer}'".inspect)
39
+ stderr_log("Cursor postition: x: #{@cursor_position[0]}, y: #{@cursor_position[1]}: #{y};#{x}H")
40
+ STDOUT.write(append_buffer)
41
+ end
42
+
43
+ def current_row
44
+ @text_content[@cursor_position[1] - 1]
45
+ end
46
+
47
+ def stderr_log(message)
48
+ unless STDERR.tty? # true when not redirecting to a file, a little janky but works for what I want
49
+ STDERR.puts(message)
50
+ end
51
+ end
52
+
53
+ def start
54
+ s = [0, 0, 0, 0].pack("S_S_S_S_")
55
+ STDOUT.ioctl(Termios::TIOCGWINSZ, s)
56
+
57
+ @height, @WIDTH, _, _ = s.unpack("S_S_S_S_")
58
+
59
+ # Raw mode
60
+ current = Termios.tcgetattr(STDIN)
61
+ t = current.dup
62
+ t.c_iflag &= ~(Termios::BRKINT | Termios::ICRNL | Termios::INPCK | Termios::ISTRIP | Termios::IXON)
63
+ t.c_oflag &= ~(Termios::OPOST)
64
+ t.c_cflag |= (Termios::CS8)
65
+ t.c_lflag &= ~(Termios::ECHO | Termios::ICANON | Termios::IEXTEN | Termios::ISIG)
66
+ t.c_cc[Termios::VMIN] = 1 # Setting 0 as in Kilo raises EOF errors
67
+ Termios.tcsetattr(STDIN, Termios::TCSANOW, t)
68
+
69
+ trap("INT") {
70
+ Termios.tcsetattr(STDIN, Termios::TCSANOW, current)
71
+ exit(0)
72
+ }
73
+
74
+ @text_content = [""]
75
+ @cursor_position = [1, 1]
76
+
77
+ loop do
78
+ refresh
79
+ c = STDIN.readpartial(1)
80
+ if c == "q"
81
+ Process.kill("INT", Process.pid)
82
+ end
83
+ # stderr_log("ord: #{c.ord}")
84
+ if c.ord == 13 # enter
85
+ if current_row && current_row.length > (@cursor_position[0] - 1)
86
+ carry = current_row[(@cursor_position[0] - 1)..-1]
87
+ current_row.slice!((@cursor_position[0] - 1)..-1)
88
+ else
89
+ carry = ""
90
+ end
91
+ if @cursor_position[1] - 1 == @text_content.length # We're on a new line at the end
92
+ new_line_index = @cursor_position[1] - 1
93
+ else
94
+ new_line_index = @cursor_position[1]
95
+ end
96
+ @text_content.insert(new_line_index, carry)
97
+ @cursor_position[0] = 1
98
+ @cursor_position[1] += 1
99
+ elsif c.ord == 127 # backspace
100
+ next if @cursor_position[0] == 1 && @cursor_position[1] == 1
101
+
102
+ if @cursor_position[0] == 1
103
+ if current_row.nil?
104
+ @text_content.delete_at(@cursor_position[1] - 1)
105
+ @cursor_position[1] -= 1
106
+ @cursor_position[0] = current_row.length + 1
107
+ elsif current_row.empty?
108
+ @text_content.delete_at(@cursor_position[1] - 1)
109
+ @cursor_position[1] -= 1
110
+ @cursor_position[0] = current_row.length + 1
111
+ else
112
+ previous_row = @text_content[@cursor_position[1] - 2]
113
+ @cursor_position[0] = previous_row.length + 1
114
+ @text_content[@cursor_position[1] - 2] = previous_row + current_row
115
+ @text_content.delete_at(@cursor_position[1] - 1)
116
+ @cursor_position[1] -= 1
117
+ end
118
+ else
119
+ deletion_index = @cursor_position[0] - 2
120
+ current_row.slice!(deletion_index)
121
+ @cursor_position[0] -= 1
122
+ end
123
+ elsif c.ord == 27 # ESC
124
+ second_char = STDIN.read_nonblock(1, exception: false)
125
+ next if second_char == :wait_readable
126
+
127
+ third_char = STDIN.read_nonblock(1, exception: false)
128
+ next if third_char == :wait_readable
129
+
130
+ if second_char == "["
131
+ case third_char
132
+ when "A" # Up
133
+ @cursor_position[1] -= 1 unless @cursor_position[1] == 1
134
+ if current_row && @cursor_position[0] > current_row.length + 1
135
+ @cursor_position[0] = current_row.length + 1
136
+ end
137
+ when "B" # Down
138
+ if @cursor_position[1] == @text_content.length
139
+ @cursor_position[0] = 1
140
+ end
141
+ @cursor_position[1] += 1 unless @cursor_position[1] == @text_content.length + 1
142
+ if current_row && @cursor_position[0] > current_row.length + 1
143
+ @cursor_position[0] = current_row.length + 1
144
+ end
145
+ when "C" # Right
146
+ # stderr_log("Current row: #{current_row}\n")
147
+ if current_row && @cursor_position[0] > current_row.length
148
+ if @cursor_position[1] <= @text_content.length + 1
149
+ @cursor_position[0] = 1
150
+ @cursor_position[1] += 1
151
+ end
152
+ elsif current_row
153
+ @cursor_position[0] += 1
154
+ end
155
+ when "D" # Left
156
+ if @cursor_position[0] == 1
157
+ if @cursor_position[1] > 1
158
+ @cursor_position[1] -= 1
159
+ @cursor_position[0] = current_row.length + 1
160
+ end
161
+ else
162
+ @cursor_position[0] -= 1
163
+ end
164
+ when "H" then "H" # Home
165
+ when "F" then "F" # End
166
+ end
167
+ end
168
+ # @text_content.last << third_char
169
+ elsif c.ord >= 32 && c.ord <= 126
170
+ if current_row.nil?
171
+ @text_content << ""
172
+ end
173
+ current_row.insert(@cursor_position[0] - 1, c)
174
+ @cursor_position[0] += 1
175
+ else
176
+ stderr_log("Ignored char: #{c.ord}")
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: giga
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Pierre Jambet
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-08-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby-termios
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.1.0
27
+ description: A console text editor, built pretty much from scratch
28
+ email: hello@pjam.me
29
+ executables:
30
+ - giga
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - LICENSE
35
+ - README.md
36
+ - bin/giga
37
+ - lib/giga.rb
38
+ - lib/giga/version.rb
39
+ homepage: https://rubygems.org/gems/giga
40
+ licenses:
41
+ - MIT
42
+ metadata: {}
43
+ post_install_message:
44
+ rdoc_options: []
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '0'
57
+ requirements: []
58
+ rubygems_version: 3.3.7
59
+ signing_key:
60
+ specification_version: 4
61
+ summary: A console text editor, pretty much copied from antirez/kilo
62
+ test_files: []