parent
2b818d0847
commit
3e15e5fa2a
@ -1,3 +1,4 @@
|
||||
use std::iter;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
@ -9,6 +10,8 @@ use gtk::prelude::*;
|
||||
use cairo;
|
||||
use pango;
|
||||
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use neovim_lib::Value;
|
||||
|
||||
use nvim::{self, NeovimClient};
|
||||
@ -55,7 +58,7 @@ impl Level {
|
||||
level
|
||||
}
|
||||
|
||||
fn replace_line(&mut self, lines: Vec<Vec<(Option<Attrs>, Vec<char>)>>, append: bool) {
|
||||
fn replace_line(&mut self, lines: Vec<Vec<(Option<Attrs>, Vec<String>)>>, append: bool) {
|
||||
if append {
|
||||
self.model_layout.layout_append(lines);
|
||||
} else {
|
||||
@ -77,34 +80,20 @@ impl Level {
|
||||
self.preferred_height = (line_height * rows as f64) as i32;
|
||||
}
|
||||
|
||||
fn to_attributed_content(
|
||||
content: &Vec<Vec<(HashMap<String, Value>, String)>>,
|
||||
) -> Vec<Vec<(Option<Attrs>, Vec<char>)>> {
|
||||
content
|
||||
.iter()
|
||||
.map(|line_chars| {
|
||||
line_chars
|
||||
.iter()
|
||||
.map(|c| (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()))
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn from_multiline_content(
|
||||
content: &Vec<Vec<(HashMap<String, Value>, String)>>,
|
||||
max_width: i32,
|
||||
render_state: &shell::RenderState,
|
||||
) -> Self {
|
||||
Level::from_lines(
|
||||
Level::to_attributed_content(content),
|
||||
content.to_attributed_content(),
|
||||
max_width,
|
||||
render_state,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_lines(
|
||||
lines: Vec<Vec<(Option<Attrs>, Vec<char>)>>,
|
||||
lines: Vec<Vec<(Option<Attrs>, Vec<String>)>>,
|
||||
max_width: i32,
|
||||
render_state: &shell::RenderState,
|
||||
) -> Self {
|
||||
@ -144,17 +133,16 @@ fn prompt_lines(
|
||||
firstc: &str,
|
||||
prompt: &str,
|
||||
indent: u64,
|
||||
) -> (usize, Vec<(Option<Attrs>, Vec<char>)>) {
|
||||
let prompt: Vec<(Option<Attrs>, Vec<char>)> = if !firstc.is_empty() {
|
||||
) -> (usize, Vec<(Option<Attrs>, Vec<String>)>) {
|
||||
let prompt: Vec<(Option<Attrs>, Vec<String>)> = if !firstc.is_empty() {
|
||||
if firstc.len() >= indent as usize {
|
||||
vec![(None, firstc.chars().collect())]
|
||||
vec![(None, vec![firstc.to_owned()])]
|
||||
} else {
|
||||
vec![
|
||||
(
|
||||
None,
|
||||
firstc
|
||||
.chars()
|
||||
.chain((firstc.len()..indent as usize).map(|_| ' '))
|
||||
iter::once(firstc.to_owned())
|
||||
.chain((firstc.len()..indent as usize).map(|_| " ".to_owned()))
|
||||
.collect(),
|
||||
),
|
||||
]
|
||||
@ -162,7 +150,7 @@ fn prompt_lines(
|
||||
} else if !prompt.is_empty() {
|
||||
prompt
|
||||
.lines()
|
||||
.map(|l| (None, l.chars().collect()))
|
||||
.map(|l| (None, l.graphemes(true).map(|g| g.to_owned()).collect()))
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
@ -446,13 +434,10 @@ impl CmdLine {
|
||||
let mut state = self.state.borrow_mut();
|
||||
let render_state = state.render_state.clone();
|
||||
{
|
||||
let attr_content = content
|
||||
.iter()
|
||||
.map(|c| (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()))
|
||||
.collect();
|
||||
let attr_content = content.to_attributed_content();
|
||||
|
||||
let block = state.block.as_mut().unwrap();
|
||||
block.replace_line(vec![attr_content], true);
|
||||
block.replace_line(attr_content, true);
|
||||
block.update_preferred_size(&*render_state.borrow());
|
||||
block.update_cache(&*render_state.borrow());
|
||||
}
|
||||
@ -594,18 +579,15 @@ pub struct CmdLineContext<'a> {
|
||||
|
||||
impl<'a> CmdLineContext<'a> {
|
||||
fn get_lines(&self) -> LineContent {
|
||||
let content_line: Vec<(Option<Attrs>, Vec<char>)> = self.content
|
||||
.iter()
|
||||
.map(|c| (Some(Attrs::from_value_map(&c.0)), c.1.chars().collect()))
|
||||
.collect();
|
||||
let mut content_line = self.content.to_attributed_content();
|
||||
let (prompt_offset, prompt_lines) = prompt_lines(&self.firstc, &self.prompt, self.indent);
|
||||
|
||||
let mut content: Vec<_> = prompt_lines.into_iter().map(|line| vec![line]).collect();
|
||||
|
||||
if content.is_empty() {
|
||||
content.push(content_line);
|
||||
content.push(content_line.remove(0));
|
||||
} else {
|
||||
content.last_mut().map(|line| line.extend(content_line));
|
||||
content.last_mut().map(|line| line.extend(content_line.remove(0)));
|
||||
}
|
||||
|
||||
LineContent {
|
||||
@ -616,6 +598,43 @@ impl<'a> CmdLineContext<'a> {
|
||||
}
|
||||
|
||||
struct LineContent {
|
||||
lines: Vec<Vec<(Option<Attrs>, Vec<char>)>>,
|
||||
lines: Vec<Vec<(Option<Attrs>, Vec<String>)>>,
|
||||
prompt_offset: usize,
|
||||
}
|
||||
|
||||
trait ToAttributedModelContent {
|
||||
fn to_attributed_content(&self) -> Vec<Vec<(Option<Attrs>, Vec<String>)>>;
|
||||
}
|
||||
|
||||
impl ToAttributedModelContent for Vec<Vec<(HashMap<String, Value>, String)>> {
|
||||
fn to_attributed_content(&self) -> Vec<Vec<(Option<Attrs>, Vec<String>)>> {
|
||||
self.iter()
|
||||
.map(|line_chars| {
|
||||
line_chars
|
||||
.iter()
|
||||
.map(|c| {
|
||||
(
|
||||
Some(Attrs::from_value_map(&c.0)),
|
||||
c.1.graphemes(true).map(|g| g.to_owned()).collect(),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToAttributedModelContent for Vec<(HashMap<String, Value>, String)> {
|
||||
fn to_attributed_content(&self) -> Vec<Vec<(Option<Attrs>, Vec<String>)>> {
|
||||
vec![
|
||||
self.iter()
|
||||
.map(|c| {
|
||||
(
|
||||
Some(Attrs::from_value_map(&c.0)),
|
||||
c.1.graphemes(true).map(|g| g.to_owned()).collect(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ extern crate phf;
|
||||
extern crate rmpv;
|
||||
extern crate regex;
|
||||
extern crate unicode_width;
|
||||
extern crate unicode_segmentation;
|
||||
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
|
@ -76,8 +76,6 @@ impl Attrs {
|
||||
}
|
||||
}
|
||||
|
||||
const EMPTY_STRING: String = String::new();
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Cell {
|
||||
pub attrs: Attrs,
|
||||
@ -87,7 +85,7 @@ pub struct Cell {
|
||||
|
||||
impl Cell {
|
||||
pub fn new_empty() -> Cell {
|
||||
Cell::new(EMPTY_STRING)
|
||||
Cell::new(" ".to_owned())
|
||||
}
|
||||
|
||||
pub fn new(ch: String) -> Cell {
|
||||
@ -99,7 +97,7 @@ impl Cell {
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.ch = EMPTY_STRING;
|
||||
self.ch = " ".to_owned();
|
||||
self.attrs.clear();
|
||||
self.dirty = true;
|
||||
}
|
||||
|
@ -402,9 +402,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_styled_line() {
|
||||
let mut line = Line::new(3);
|
||||
line[0].ch = 'a';
|
||||
line[1].ch = 'b';
|
||||
line[2].ch = 'c';
|
||||
line[0].ch = "a".to_owned();
|
||||
line[1].ch = "b".to_owned();
|
||||
line[2].ch = "c".to_owned();
|
||||
|
||||
let styled_line = StyledLine::from(&line, &color::ColorModel::new());
|
||||
assert_eq!("abc", styled_line.line_str);
|
||||
|
@ -305,7 +305,7 @@ mod tests {
|
||||
|
||||
model.set_cursor(1, 1);
|
||||
|
||||
let rect = model.put(' ', false, None);
|
||||
let rect = model.put(" ".to_owned(), false, None);
|
||||
|
||||
assert_eq!(1, rect.top);
|
||||
assert_eq!(1, rect.left);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::cmp::max;
|
||||
|
||||
use unicode_width::UnicodeWidthChar;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use ui_model::{Attrs, UiModel};
|
||||
|
||||
@ -94,6 +94,7 @@ impl ModelLayout {
|
||||
if cur_col < col_idx + chars.len() {
|
||||
let col_sub_idx = cur_col - col_idx;
|
||||
chars.insert(col_sub_idx, ch);
|
||||
break;
|
||||
} else {
|
||||
col_idx += chars.len();
|
||||
}
|
||||
@ -117,7 +118,7 @@ impl ModelLayout {
|
||||
for content in lines {
|
||||
for &(ref attr, ref ch_list) in content {
|
||||
for ch in ch_list {
|
||||
let ch_width = ch.width().unwrap_or(1);
|
||||
let ch_width = max(1, ch.width());
|
||||
|
||||
if col_idx + ch_width > self.model.columns {
|
||||
col_idx = 0;
|
||||
@ -125,9 +126,9 @@ impl ModelLayout {
|
||||
}
|
||||
|
||||
self.model.set_cursor(row_idx, col_idx as usize);
|
||||
self.model.put(*ch, false, attr.as_ref());
|
||||
self.model.put(ch.clone(), false, attr.as_ref());
|
||||
if ch_width > 1 {
|
||||
self.model.put(' ', true, attr.as_ref());
|
||||
self.model.put(" ".to_owned(), true, attr.as_ref());
|
||||
}
|
||||
|
||||
if max_col_idx < col_idx {
|
||||
@ -169,7 +170,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_count_lines() {
|
||||
let lines = vec![vec![(None, vec!['a'; 5])]];
|
||||
let lines = vec![vec![(None, vec!["a".to_owned(); 5])]];
|
||||
|
||||
let rows = ModelLayout::count_lines(&lines, 4);
|
||||
assert_eq!(2, rows);
|
||||
@ -177,7 +178,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_resize() {
|
||||
let lines = vec![vec![(None, vec!['a'; 5])]; ModelLayout::ROWS_STEP];
|
||||
let lines = vec![vec![(None, vec!["a".to_owned(); 5])]; ModelLayout::ROWS_STEP];
|
||||
let mut model = ModelLayout::new(5);
|
||||
|
||||
model.layout(lines.clone());
|
||||
@ -194,14 +195,14 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_cols_filled() {
|
||||
let lines = vec![vec![(None, vec!['a'; 3])]; 1];
|
||||
let lines = vec![vec![(None, vec!["a".to_owned(); 3])]; 1];
|
||||
let mut model = ModelLayout::new(5);
|
||||
|
||||
model.layout(lines);
|
||||
let (cols, _) = model.size();
|
||||
assert_eq!(4, cols); // size is 3 and 4 - is with cursor position
|
||||
|
||||
let lines = vec![vec![(None, vec!['a'; 2])]; 1];
|
||||
let lines = vec![vec![(None, vec!["a".to_owned(); 2])]; 1];
|
||||
|
||||
model.layout_append(lines);
|
||||
let (cols, _) = model.size();
|
||||
@ -210,35 +211,35 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_insert_shift() {
|
||||
let lines = vec![vec![(None, vec!['a'; 3])]; 1];
|
||||
let lines = vec![vec![(None, vec!["a".to_owned(); 3])]; 1];
|
||||
let mut model = ModelLayout::new(5);
|
||||
model.layout(lines);
|
||||
model.set_cursor(1);
|
||||
|
||||
model.insert_char("b", true);
|
||||
model.insert_char("b".to_owned(), true);
|
||||
|
||||
let (cols, _) = model.size();
|
||||
assert_eq!(4, cols);
|
||||
assert_eq!('b', model.model.model()[0].line[1].ch);
|
||||
assert_eq!("b", model.model.model()[0].line[1].ch);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_no_shift() {
|
||||
let lines = vec![vec![(None, vec!['a'; 3])]; 1];
|
||||
let lines = vec![vec![(None, vec!["a".to_owned(); 3])]; 1];
|
||||
let mut model = ModelLayout::new(5);
|
||||
model.layout(lines);
|
||||
model.set_cursor(1);
|
||||
|
||||
model.insert_char("b", false);
|
||||
model.insert_char("b".to_owned(), false);
|
||||
|
||||
let (cols, _) = model.size();
|
||||
assert_eq!(3, cols);
|
||||
assert_eq!('b', model.model.model()[0].line[1].ch);
|
||||
assert_eq!("b", model.model.model()[0].line[1].ch);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_double_width() {
|
||||
let lines = vec![vec![(None, vec!['あ'; 3])]; 1];
|
||||
let lines = vec![vec![(None, vec!["あ".to_owned(); 3])]; 1];
|
||||
let mut model = ModelLayout::new(7);
|
||||
model.layout(lines);
|
||||
model.set_cursor(1);
|
||||
|
Loading…
Reference in New Issue
Block a user