diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs
index c55d4c98..5f350671 100644
--- a/helix-term/src/application.rs
+++ b/helix-term/src/application.rs
@@ -15,7 +15,7 @@ use std::{
 use anyhow::Error;
 
 use crossterm::{
-    event::{Event, EventStream},
+    event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream},
     execute, terminal,
 };
 
@@ -449,6 +449,9 @@ impl Application {
         let mut stdout = stdout();
 
         execute!(stdout, terminal::EnterAlternateScreen)?;
+        if self.config.terminal.mouse {
+            execute!(stdout, EnableMouseCapture)?;
+        }
 
         // Exit the alternate screen and disable raw mode before panicking
         let hook = std::panic::take_hook();
@@ -456,6 +459,7 @@ impl Application {
             // We can't handle errors properly inside this closure.  And it's
             // probably not a good idea to `unwrap()` inside a panic handler.
             // So we just ignore the `Result`s.
+            let _ = execute!(std::io::stdout(), DisableMouseCapture);
             let _ = execute!(std::io::stdout(), terminal::LeaveAlternateScreen);
             let _ = terminal::disable_raw_mode();
             hook(info);
@@ -468,6 +472,7 @@ impl Application {
         // reset cursor shape
         write!(stdout, "\x1B[2 q")?;
 
+        execute!(stdout, DisableMouseCapture)?;
         execute!(stdout, terminal::LeaveAlternateScreen)?;
 
         terminal::disable_raw_mode()?;
diff --git a/helix-term/src/config.rs b/helix-term/src/config.rs
index f3f0ba53..38cd3bfb 100644
--- a/helix-term/src/config.rs
+++ b/helix-term/src/config.rs
@@ -9,6 +9,8 @@ pub struct Config {
     pub lsp: LspConfig,
     #[serde(default)]
     pub keys: Keymaps,
+    #[serde(default)]
+    pub terminal: TerminalConfig,
 }
 
 #[derive(Debug, Default, Clone, PartialEq, Deserialize)]
@@ -17,6 +19,18 @@ pub struct LspConfig {
     pub display_messages: bool,
 }
 
+#[derive(Debug, Clone, PartialEq, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct TerminalConfig {
+    pub mouse: bool,
+}
+
+impl Default for TerminalConfig {
+    fn default() -> Self {
+        Self { mouse: true }
+    }
+}
+
 #[test]
 fn parsing_keymaps_config_file() {
     use crate::keymap;
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index d5c907b8..ec5687bd 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -12,7 +12,7 @@ use helix_core::{
     syntax::{self, HighlightEvent},
     unicode::segmentation::UnicodeSegmentation,
     unicode::width::UnicodeWidthStr,
-    LineEnding, Position, Range,
+    LineEnding, Position, Range, Selection,
 };
 use helix_view::{
     document::Mode,
@@ -24,7 +24,7 @@ use helix_view::{
 };
 use std::borrow::Cow;
 
-use crossterm::event::Event;
+use crossterm::event::{Event, MouseButton, MouseEvent, MouseEventKind};
 use tui::buffer::Buffer as Surface;
 
 pub struct EditorView {
@@ -805,6 +805,70 @@ impl Component for EditorView {
 
                 EventResult::Consumed(callback)
             }
+            Event::Mouse(MouseEvent {
+                kind: MouseEventKind::Down(MouseButton::Left),
+                row,
+                column,
+                modifiers,
+                ..
+            }) => {
+                let editor = &mut cx.editor;
+
+                let result = editor.tree.views().find_map(|(view, _focus)| {
+                    view.pos_at_screen_coords(
+                        &editor.documents[view.doc],
+                        row as usize,
+                        column as usize,
+                    )
+                    .map(|pos| (pos, view.id))
+                });
+
+                if let Some((pos, id)) = result {
+                    let doc = &mut editor.documents[editor.tree.get(id).doc];
+                    let jump = (doc.id(), doc.selection(id).clone());
+                    editor.tree.get_mut(id).jumps.push(jump);
+
+                    if modifiers == crossterm::event::KeyModifiers::ALT {
+                        let selection = doc.selection(id).clone();
+                        doc.set_selection(id, selection.push(Range::point(pos)));
+                    } else {
+                        doc.set_selection(id, Selection::point(pos));
+                    }
+
+                    editor.tree.focus = id;
+
+                    return EventResult::Consumed(None);
+                }
+
+                EventResult::Ignored
+            }
+
+            Event::Mouse(MouseEvent {
+                kind: MouseEventKind::Drag(MouseButton::Left),
+                row,
+                column,
+                ..
+            }) => {
+                let (view, doc) = current!(cx.editor);
+
+                let pos = view.pos_at_screen_coords(doc, row as usize, column as usize);
+
+                if pos == None {
+                    return EventResult::Ignored;
+                }
+
+                let selection = doc.selection(view.id).clone();
+                let primary_anchor = selection.primary().anchor;
+                let new_selection = selection.transform(|range| -> Range {
+                    if range.anchor == primary_anchor {
+                        return Range::new(primary_anchor, pos.unwrap());
+                    }
+                    range
+                });
+
+                doc.set_selection(view.id, new_selection);
+                EventResult::Consumed(None)
+            }
             Event::Mouse(_) => EventResult::Ignored,
         }
     }
diff --git a/helix-view/src/view.rs b/helix-view/src/view.rs
index 6b0c3c2a..d61fbe4a 100644
--- a/helix-view/src/view.rs
+++ b/helix-view/src/view.rs
@@ -4,6 +4,7 @@ use crate::{graphics::Rect, Document, DocumentId, ViewId};
 use helix_core::{
     coords_at_pos,
     graphemes::{grapheme_width, RopeGraphemes},
+    line_ending::line_end_char_index,
     Position, RopeSlice, Selection,
 };
 
@@ -165,6 +166,74 @@ impl View {
         Some(Position::new(row, col))
     }
 
+    /// Verifies whether a screen position is inside the view
+    /// Returns true when position is inside the view
+    pub fn verify_screen_coords(&self, row: usize, column: usize) -> bool {
+        // 2 for status
+        if row < self.area.y as usize || row > self.area.y as usize + self.area.height as usize - 2
+        {
+            return false;
+        }
+
+        // TODO: not ideal
+        const OFFSET: usize = 7; // 1 diagnostic + 5 linenr + 1 gutter
+
+        if column < self.area.x as usize + OFFSET
+            || column > self.area.x as usize + self.area.width as usize
+        {
+            return false;
+        }
+        true
+    }
+
+    pub fn text_pos_at_screen_coords(
+        &self,
+        text: &RopeSlice,
+        row: usize,
+        column: usize,
+        tab_width: usize,
+    ) -> Option<usize> {
+        if !self.verify_screen_coords(row, column) {
+            return None;
+        }
+
+        let line_number = row - self.area.y as usize + self.first_line;
+
+        if line_number > text.len_lines() - 1 {
+            return Some(text.len_chars());
+        }
+
+        let mut pos = text.line_to_char(line_number);
+
+        let current_line = text.line(line_number);
+
+        // TODO: not ideal
+        const OFFSET: usize = 7; // 1 diagnostic + 5 linenr + 1 gutter
+
+        let target = column - OFFSET - self.area.x as usize + self.first_col;
+        let mut selected = 0;
+
+        for grapheme in RopeGraphemes::new(current_line) {
+            if selected >= target {
+                break;
+            }
+            if grapheme == "\t" {
+                selected += tab_width;
+            } else {
+                let width = grapheme_width(&Cow::from(grapheme));
+                selected += width;
+            }
+            pos += grapheme.chars().count();
+        }
+
+        Some(pos.min(line_end_char_index(&text.slice(..), line_number)))
+    }
+
+    /// Translates a screen position to position in the text document.
+    /// Returns a usize typed position in bounds of the text if found in this view, None if out of view.
+    pub fn pos_at_screen_coords(&self, doc: &Document, row: usize, column: usize) -> Option<usize> {
+        self.text_pos_at_screen_coords(&doc.text().slice(..), row, column, doc.tab_width())
+    }
     // pub fn traverse<F>(&self, text: RopeSlice, start: usize, end: usize, fun: F)
     // where
     //     F: Fn(usize, usize),
@@ -186,3 +255,81 @@ impl View {
     //     }
     // }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use helix_core::Rope;
+
+    #[test]
+    fn test_text_pos_at_screen_coords() {
+        let mut view = View::new(DocumentId::default());
+        view.area = Rect::new(40, 40, 40, 40);
+        let text = Rope::from_str("abc\n\tdef");
+
+        assert_eq!(
+            view.text_pos_at_screen_coords(&text.slice(..), 40, 2, 4),
+            None
+        );
+
+        assert_eq!(
+            view.text_pos_at_screen_coords(&text.slice(..), 40, 41, 4),
+            None
+        );
+
+        assert_eq!(
+            view.text_pos_at_screen_coords(&text.slice(..), 0, 2, 4),
+            None
+        );
+
+        assert_eq!(
+            view.text_pos_at_screen_coords(&text.slice(..), 0, 49, 4),
+            None
+        );
+
+        assert_eq!(
+            view.text_pos_at_screen_coords(&text.slice(..), 0, 41, 4),
+            None
+        );
+
+        assert_eq!(
+            view.text_pos_at_screen_coords(&text.slice(..), 40, 81, 4),
+            None
+        );
+
+        assert_eq!(
+            view.text_pos_at_screen_coords(&text.slice(..), 78, 41, 4),
+            None
+        );
+
+        assert_eq!(
+            view.text_pos_at_screen_coords(&text.slice(..), 40, 40 + 7 + 3, 4),
+            Some(3)
+        );
+
+        assert_eq!(
+            view.text_pos_at_screen_coords(&text.slice(..), 40, 80, 4),
+            Some(3)
+        );
+
+        assert_eq!(
+            view.text_pos_at_screen_coords(&text.slice(..), 41, 40 + 7 + 1, 4),
+            Some(5)
+        );
+
+        assert_eq!(
+            view.text_pos_at_screen_coords(&text.slice(..), 41, 40 + 7 + 4, 4),
+            Some(5)
+        );
+
+        assert_eq!(
+            view.text_pos_at_screen_coords(&text.slice(..), 41, 40 + 7 + 7, 4),
+            Some(8)
+        );
+
+        assert_eq!(
+            view.text_pos_at_screen_coords(&text.slice(..), 41, 80, 4),
+            Some(8)
+        );
+    }
+}