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