giga 0.0.0

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.
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: []