Skip to content

Commit

Permalink
Added nonprintable characters rendering
Browse files Browse the repository at this point in the history
I added a new field for Editor struct 'non_printable' that
changes the way rows are rendered (using CTRL+P). During
testing, I found out that Kibi has a little bug when starting
a new editor, because it adds a newline from the start on line
2, even though there is nothing and the user did not insert a
new line. This can be shown by using the key bind above, I will
look into it. The whitespaces are now rendered as '·', new line
is now rendered as '␊' (and it doesn't affect the normal
characters of the row), tab is now rendered as a '├──┤' of
different lenghts. Other control characters can be found in
the `draw` function.
I know that the number of lines is not under 1024, I tried to
mess around with the fmt file, but I couldn't force it to
compress some if statements that it had compressed before.
I will try to refactor as much as possible, but the current
line count may be too close to the limit for any other features.
  • Loading branch information
RaresCon committed Jan 16, 2023
1 parent d79e32f commit a2610a5
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 9 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ kibi --version # Print version information and exit
| Ctrl-C | Copies the entire line |
| Ctrl-X | Cuts the entire line |
| Ctrl-V | Will paste the copied line |
| Ctrl-P | Show/Hide nonprintable characters |

### Configuration

Expand All @@ -218,6 +219,8 @@ quit_times=2
message_duration=3
# Whether to show line numbers.
show_line_numbers=true
# Whether to show nonprintable characters
non_printable=false
```

The location of these files is described below.
Expand Down
3 changes: 3 additions & 0 deletions config_example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ message_duration = 3

# Whether to display line numbers.
show_line_numbers = true

# Whether to show nonprintable characters
non_printable = false
10 changes: 9 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,20 @@ pub struct Config {
pub message_dur: Duration,
/// Whether to display line numbers.
pub show_line_num: bool,
/// Whether to display nonprintable characters.
pub non_printable: bool,
}

impl Default for Config {
/// Default configuration.
fn default() -> Self {
Self { tab_stop: 4, quit_times: 2, message_dur: Duration::new(3, 0), show_line_num: true }
Self {
tab_stop: 4,
quit_times: 2,
message_dur: Duration::new(3, 0),
show_line_num: true,
non_printable: false,
}
}
}

Expand Down
13 changes: 10 additions & 3 deletions src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const PASTE: u8 = ctrl_key(b'V');
const DUPLICATE: u8 = ctrl_key(b'D');
const EXECUTE: u8 = ctrl_key(b'E');
const REMOVE_LINE: u8 = ctrl_key(b'R');
const NON_PRINTABLE: u8 = ctrl_key(b'P');
const BACKSPACE: u8 = 127;

const HELP_MESSAGE: &str =
Expand Down Expand Up @@ -122,6 +123,8 @@ pub struct Editor {
orig_term_mode: Option<sys::TermMode>,
/// The copied buffer of a row
copied_row: Vec<u8>,
/// Whether to display nonprintable characters.
non_printable: bool,
}

/// Describes a status message, shown at the bottom at the screen.
Expand Down Expand Up @@ -317,7 +320,7 @@ impl Editor {
let mut hl_state = if y > 0 { self.rows[y - 1].hl_state } else { HlState::Normal };
for row in self.rows.iter_mut().skip(y) {
let previous_hl_state = row.hl_state;
hl_state = row.update(&self.syntax, hl_state, self.config.tab_stop);
hl_state = row.update(&self.syntax, self.non_printable, hl_state, self.config.tab_stop);
if ignore_following_rows || hl_state == previous_hl_state {
return;
}
Expand All @@ -330,7 +333,7 @@ impl Editor {
fn update_all_rows(&mut self) {
let mut hl_state = HlState::Normal;
for row in &mut self.rows {
hl_state = row.update(&self.syntax, hl_state, self.config.tab_stop);
hl_state = row.update(&self.syntax, self.non_printable, hl_state, self.config.tab_stop);
}
}

Expand Down Expand Up @@ -529,7 +532,7 @@ impl Editor {
if let Some(row) = row {
// Draw a row of text
self.draw_left_padding(buffer, i + 1)?;
row.draw(self.cursor.coff, self.screen_cols, buffer)?;
row.draw(self.cursor.coff, self.non_printable, self.screen_cols, buffer)?;
} else {
// Draw an empty row
self.draw_left_padding(buffer, '~')?;
Expand Down Expand Up @@ -645,6 +648,10 @@ impl Editor {
}
Key::Char(COPY) => self.copy_current_row(),
Key::Char(PASTE) => self.paste_current_row(),
Key::Char(NON_PRINTABLE) => {
self.non_printable = !self.non_printable;
self.update_all_rows();
}
Key::Char(EXECUTE) => prompt_mode = Some(PromptMode::Execute(String::new())),
Key::Char(c) => self.insert_byte(*c),
}
Expand Down
58 changes: 53 additions & 5 deletions src/row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ impl Row {
// TODO: Combine update and update_syntax
/// Update the row: convert tabs into spaces and compute highlight symbols
/// The `hl_state` argument is the `HLState` for the previous row.
pub fn update(&mut self, syntax: &SyntaxConf, hl_state: HlState, tab: usize) -> HlState {
pub fn update(
&mut self, syntax: &SyntaxConf, non_printable: bool, hl_state: HlState, tab: usize,
) -> HlState {
self.render.clear();
self.cx2rx.clear();
self.rx2cx.clear();
Expand All @@ -62,7 +64,23 @@ impl Row {
let n_bytes = c.len_utf8();
// The number of rendered characters
let n_rend_chars = if c == '\t' { tab - (rx % tab) } else { c.width().unwrap_or(1) };
self.render.push_str(&(if c == '\t' { " ".repeat(n_rend_chars) } else { c.into() }));
self.render.push_str(
&(if c == '\t' && non_printable {
let mut str;
if n_rend_chars > 1 {
str = String::from('├');
str.push_str(&"─".repeat(n_rend_chars - 2));
str.push('┤');
} else {
str = String::from('─');
}
str
} else if c == '\t' {
" ".repeat(n_rend_chars)
} else {
c.into()
}),
);
self.cx2rx.extend(std::iter::repeat(rx).take(n_bytes));
self.rx2cx.extend(std::iter::repeat(cx).take(n_rend_chars));
rx += n_rend_chars;
Expand Down Expand Up @@ -176,13 +194,35 @@ impl Row {
/// Draw the row and write the result to a buffer. An `offset` can be given, as well as a limit
/// on the length of the row (`max_len`). After writing the characters, clear the rest of the
/// line and move the cursor to the start of the next line.
pub fn draw(&self, offset: usize, max_len: usize, buffer: &mut String) -> Result<(), Error> {
pub fn draw(
&self, offset: usize, non_printable: bool, max_len: usize, buffer: &mut String,
) -> Result<(), Error> {
let mut current_hl_type = HlType::Normal;
let chars = self.render.chars().skip(offset).take(max_len);
let mut rx = self.render.chars().take(offset).map(|c| c.width().unwrap_or(1)).sum();
let mut rendered_char;
for (c, mut hl_type) in chars.zip(self.hl.iter().skip(offset)) {
if c.is_ascii_control() {
let rendered_char = if (c as u8) <= 26 { (b'@' + c as u8) as char } else { '?' };
if !non_printable {
if (c as u8) <= 26 {
rendered_char = (b'@' + c as u8) as char
} else {
rendered_char = '?'
}
} else {
match c {
'\x0D' => rendered_char = '␍',
// null
'\x00' => rendered_char = '␀',
// bell
'\x07' => rendered_char = '␇',
// backspace
'\x08' => rendered_char = '␈',
// escape
'\x1B' => rendered_char = '␛',
_ => rendered_char = '?',
}
};
write!(buffer, "{REVERSE_VIDEO}{rendered_char}{RESET_FMT}")?;
// Restore previous color
if current_hl_type != HlType::Normal {
Expand All @@ -202,10 +242,18 @@ impl Row {
buffer.push_str(&hl_type.to_string());
current_hl_type = *hl_type;
}
buffer.push(c);
if c.is_ascii_whitespace() && non_printable {
rendered_char = '·';
} else {
rendered_char = c;
}
buffer.push(rendered_char);
}
rx += c.width().unwrap_or(1);
}
if non_printable {
buffer.push('␊')
}
buffer.push_str(RESET_FMT);
Ok(())
}
Expand Down

0 comments on commit a2610a5

Please sign in to comment.