aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.rs1
-rw-r--r--src/event.rs1
-rw-r--r--src/input.rs11
-rw-r--r--src/selection.rs41
-rw-r--r--src/term/cell.rs43
-rw-r--r--src/term/mod.rs82
6 files changed, 171 insertions, 8 deletions
diff --git a/src/config.rs b/src/config.rs
index 9bdcc6e6..abf0bdf5 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -280,6 +280,7 @@ impl de::Deserialize for ActionWrapper {
{
Ok(ActionWrapper(match value {
"Paste" => Action::Paste,
+ "Copy" => Action::Copy,
"PasteSelection" => Action::PasteSelection,
_ => return Err(E::invalid_value("invalid value for Action")),
}))
diff --git a/src/event.rs b/src/event.rs
index 0c4c701b..a231e898 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -148,7 +148,6 @@ impl<N: Notify> Processor<N> {
},
glutin::Event::KeyboardInput(state, _code, key, mods, string) => {
processor.process_key(state, key, mods, string);
- processor.ctx.selection.clear();
},
glutin::Event::MouseInput(state, button) => {
processor.mouse_input(state, button);
diff --git a/src/input.rs b/src/input.rs
index 92379be9..4339b69f 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -148,8 +148,14 @@ impl Action {
ctx.notifier.notify(s.clone().into_bytes())
},
Action::Copy => {
- // so... need access to terminal state. and the selection.
- unimplemented!();
+ if let Some(selection) = ctx.selection.span() {
+ let buf = ctx.terminal.string_from_selection(&selection);
+
+ Clipboard::new()
+ .expect("get clipboard")
+ .store_primary(buf)
+ .expect("copy into clipboard");
+ }
},
Action::Paste |
Action::PasteSelection => {
@@ -328,6 +334,7 @@ impl<'a, N: Notify + 'a> Processor<'a, N> {
// Didn't process a binding; print the provided character
if let Some(string) = string {
self.ctx.notifier.notify(string.into_bytes());
+ self.ctx.selection.clear();
}
}
}
diff --git a/src/selection.rs b/src/selection.rs
index 6c927967..a02d94e4 100644
--- a/src/selection.rs
+++ b/src/selection.rs
@@ -21,7 +21,7 @@
use std::mem;
use std::ops::RangeInclusive;
-use index::{Location, Column, Side, Linear};
+use index::{Location, Column, Side, Linear, Line};
use grid::ToRange;
/// The area selected
@@ -94,7 +94,7 @@ impl Selection {
pub fn span(&self) -> Option<Span> {
match *self {
- Selection::Active {ref start, ref end, ref start_side, ref end_side } => {
+ Selection::Active { ref start, ref end, ref start_side, ref end_side } => {
let (front, tail, front_side, tail_side) = if *start > *end {
// Selected upward; start/end are swapped
(end, start, end_side, start_side)
@@ -180,6 +180,43 @@ pub struct Span {
}
impl Span {
+ pub fn to_locations(&self, cols: Column) -> (Location, Location) {
+ match self.ty {
+ SpanType::Inclusive => (self.front, self.tail),
+ SpanType::Exclusive => {
+ (Span::wrap_start(self.front, cols), Span::wrap_end(self.tail, cols))
+ },
+ SpanType::ExcludeFront => (Span::wrap_start(self.front, cols), self.tail),
+ SpanType::ExcludeTail => (self.front, Span::wrap_end(self.tail, cols))
+ }
+ }
+
+ fn wrap_start(mut start: Location, cols: Column) -> Location {
+ if start.col == cols - 1 {
+ Location {
+ line: start.line + 1,
+ col: Column(0),
+ }
+ } else {
+ start.col += 1;
+ start
+ }
+ }
+
+ fn wrap_end(end: Location, cols: Column) -> Location {
+ if end.col == Column(0) && end.line != Line(0) {
+ Location {
+ line: end.line - 1,
+ col: cols
+ }
+ } else {
+ Location {
+ line: end.line,
+ col: end.col - 1
+ }
+ }
+ }
+
#[inline]
fn exclude_start(start: Linear) -> Linear {
start + 1
diff --git a/src/term/cell.rs b/src/term/cell.rs
index df648294..a95875bf 100644
--- a/src/term/cell.rs
+++ b/src/term/cell.rs
@@ -11,11 +11,11 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-
-
use std::mem;
use ansi::{NamedColor, Color};
+use grid;
+use index::Column;
bitflags! {
#[derive(Serialize, Deserialize)]
@@ -35,6 +35,27 @@ pub struct Cell {
pub flags: Flags,
}
+/// Get the length of occupied cells in a line
+pub trait LineLength {
+ /// Calculate the occupied line length
+ fn line_length(&self) -> Column;
+}
+
+impl LineLength for grid::Row<Cell> {
+ fn line_length(&self) -> Column {
+ let mut length = Column(0);
+
+ for (index, cell) in self[..].iter().rev().enumerate() {
+ if cell.c != ' ' {
+ length = Column(self.len() - index);
+ break;
+ }
+ }
+
+ length
+ }
+}
+
impl Cell {
pub fn bold(&self) -> bool {
self.flags.contains(BOLD)
@@ -67,3 +88,21 @@ impl Cell {
mem::swap(&mut self.fg, &mut self.bg);
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::{Cell, LineLength};
+
+ use grid::Row;
+ use index::Column;
+ use ansi::Color;
+
+ #[test]
+ fn line_length_works() {
+ let template = Cell::new(' ', Color::Indexed(0), Color::Indexed(0));
+ let mut row = Row::new(Column(10), &template);
+ row[Column(5)].c = 'a';
+
+ assert_eq!(row.line_length(), Column(6));
+ }
+}
diff --git a/src/term/mod.rs b/src/term/mod.rs
index b25e8382..28e73b19 100644
--- a/src/term/mod.rs
+++ b/src/term/mod.rs
@@ -21,10 +21,11 @@ use std::io;
use ansi::{self, Color, NamedColor, Attr, Handler};
use grid::{Grid, ClearRegion, ToRange};
use index::{self, Cursor, Column, Line, Linear};
-use selection::Selection;
+use selection::{Span, Selection};
pub mod cell;
pub use self::cell::Cell;
+use self::cell::LineLength;
/// Iterator that yields cells needing render
///
@@ -309,6 +310,85 @@ impl Term {
}
}
+ pub fn string_from_selection(&self, span: &Span) -> String {
+ trait Append<T> {
+ fn append(&mut self, grid: &Grid<Cell>, line: Line, cols: T);
+ }
+
+ use std::ops::{Range, RangeTo, RangeFrom, RangeFull};
+
+ impl Append<Range<Column>> for String {
+ fn append(&mut self, grid: &Grid<Cell>, line: Line, cols: Range<Column>) {
+ let line = &grid[line];
+ let line_length = line.line_length();
+ let line_end = cmp::min(line_length, cols.end + 1);
+ for cell in &line[cols.start..line_end] {
+ self.push(cell.c);
+ }
+ }
+ }
+
+ impl Append<RangeTo<Column>> for String {
+ #[inline]
+ fn append(&mut self, grid: &Grid<Cell>, line: Line, cols: RangeTo<Column>) {
+ self.append(grid, line, Column(0)..cols.end);
+ }
+ }
+
+ impl Append<RangeFrom<Column>> for String {
+ #[inline]
+ fn append(&mut self, grid: &Grid<Cell>, line: Line, cols: RangeFrom<Column>) {
+ self.append(grid, line, cols.start..Column(usize::max_value() - 1));
+ self.push('\n');
+ }
+ }
+
+ impl Append<RangeFull> for String {
+ #[inline]
+ fn append(&mut self, grid: &Grid<Cell>, line: Line, _: RangeFull) {
+ self.append(grid, line, Column(0)..Column(usize::max_value() - 1));
+ self.push('\n');
+ }
+ }
+
+ let mut res = String::new();
+
+ let (start, end) = span.to_locations(self.grid.num_cols());
+ let line_count = end.line - start.line;
+
+ match line_count {
+ // Selection within single line
+ Line(0) => {
+ res.append(&self.grid, start.line, start.col..end.col);
+ },
+
+ // Selection ends on line following start
+ Line(1) => {
+ // Starting line
+ res.append(&self.grid, start.line, start.col..);
+
+ // Ending line
+ res.append(&self.grid, end.line, ..end.col);
+ },
+
+ // Multi line selection
+ _ => {
+ // Starting line
+ res.append(&self.grid, start.line, start.col..);
+
+ let middle_range = (start.line + 1)..(end.line);
+ for line in middle_range {
+ res.append(&self.grid, line, ..);
+ }
+
+ // Ending line
+ res.append(&self.grid, end.line, ..end.col);
+ }
+ }
+
+ res
+ }
+
/// Convert the given pixel values to a grid coordinate
///
/// The mouse coordinates are expected to be relative to the top left. The