<commit_before>extern crate serde;
#[macro_use]
extern crate serde_derive;
pub extern crate neon;
pub extern crate neon_serde;
extern crate fall_tree;
pub extern crate fall_editor;

use std::iter;
use std::sync::Arc;

use neon::vm::{Call, JsResult, Lock};
use neon::mem::Handle;
use neon::js::{JsString, JsValue, JsFunction};
use neon::js::class::{Class, JsClass};

use fall_tree::{TextEditBuilder, Text, TextRange, TextEdit, TextEditOp, tu};
use fall_editor::EditorFileImpl;

mod support;

pub use self::support::{arg, ret};

pub struct EditorFile<I: EditorFileImpl> {
    imp: Arc<I>
}

impl<I: EditorFileImpl> Clone for EditorFile<I> {
    fn clone(&self) -> Self {
        EditorFile { imp: self.imp.clone() }
    }
}

impl<I: EditorFileImpl> ::std::ops::Deref for EditorFile<I> {
    type Target = I;

    fn deref(&self) -> &I {
        self.imp.deref()
    }
}

impl<I: EditorFileImpl> EditorFile<I> {
    pub fn parse(text: &str) -> Self {
        EditorFile { imp: Arc::new(I::parse(text)) }
    }
    pub fn edit(prev: &EditorFile<I>, edit: &TextEdit) -> Self {
        let imp = prev.imp.edit(edit);
        EditorFile { imp: Arc::new(imp) }
    }
}

#[macro_export]
macro_rules! declare_editor_file_class {
    ($cls:ident, $t:tt) => {
        type EditorFileHack = $crate::EditorFile<$t>;

        declare_types! {
            pub class $cls for EditorFileHack {
                init(call) {
                    use $crate::neon::js::{JsString};
                    use $crate::neon::mem::Handle;
                    use $crate::neon::vm::Lock;

                    let scope = call.scope;
                    let file = match call.arguments.len() {
                        1 => {
                            let text = call.arguments.require(scope, 0)?.check::<JsString>()?.value();
                            $crate::EditorFile::parse(&text)
                        }
                        2 => {
                            let mut file: Handle<$cls> = call.arguments.require(scope, 0)?.check::<$cls>()?;
                            let edits = call.arguments.require(scope, 1)?;
                            let edits: Vec<$crate::VsEdit> = $crate::neon_serde::from_value(scope, edits)?;
                            file.grab(|file| {
                                let inner: &$t = file;
                                let edit = $crate::from_vs_edits(
                                    <$t as $crate::fall_editor::EditorFileImpl>::file(inner).text(),
                                     edits
                                );
                                $crate::EditorFile::edit(file, &edit)
                            })
                        }
                        _ => unreachable!()
                    };
                    Ok(file)
                }
            }
        }
    }
}

pub fn parse<C: Class>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let text = call.arguments.require(scope, 0)?.check::<JsString>()?;

    let class: Handle<JsClass<C>> = C::class(scope)?;
    let ctor: Handle<JsFunction<C>> = class.constructor(scope)?;
    let ctor_args = iter::once(text.upcast());
    let file = ctor.construct::<_, JsValue, _>(scope, ctor_args)?;
    Ok(file.upcast())
}

pub fn edit<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;

    let editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let edits = call.arguments.require(scope, 1)?;

    let class: Handle<JsClass<C>> = C::class(scope)?;
    let constructor: Handle<JsFunction<C>> = class.constructor(scope)?;
    let args = iter::once(editor_file.upcast()).chain(iter::once(edits));
    let file = constructor.construct(scope, args)?;
    Ok(file.upcast())
}

pub fn metrics<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let result = editor_file.grab(|file| file.metrics());
    ret(scope, result)
}

pub fn syntax_tree<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let result = editor_file.grab(|file| file.syntax_tree());
    ret(scope, result)
}

pub fn extend_selection<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let range: TextRange = arg(scope, &call.arguments, 1)?;
    let result = editor_file.grab(|file| file.extend_selection(range));
    ret(scope, result)
}

pub fn structure<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let result = editor_file.grab(|file| file.structure());
    ret(scope, result)
}

pub fn reformat<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let result = editor_file.grab(|file| {
        let edits = file.reformat();
        to_vs_edits(edits)
    });
    ret(scope, result)
}

pub fn highlight<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let result = editor_file.grab(|file| file.highlight());
    ret(scope, result)
}

pub fn diagnostics<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let result = editor_file.grab(|file| file.diagnostics());
    ret(scope, result)
}

pub fn context_actions<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let range: TextRange = arg(scope, &call.arguments, 1)?;
    let result = editor_file.grab(|file| file.context_actions(range));
    ret(scope, result)
}

pub fn apply_context_action<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let range: TextRange = arg(scope, &call.arguments, 1)?;
    let id: String = arg(scope, &call.arguments, 2)?;
    let result = editor_file.grab(move |file| {
        file.apply_context_action(range, &id).map(to_vs_edits)
    });
    ret(scope, result)
}

#[derive(Serialize, Deserialize)]
pub struct VsEdit {
    delete: TextRange,
    insert: String,
}

pub fn from_vs_edits(text: Text, edits: Vec<VsEdit>) -> TextEdit {
    let mut edit = TextEditBuilder::new(text);
    for e in edits {
        edit.replace(e.delete, e.insert)
    }
    edit.build()
}

pub fn to_vs_edits(edit: TextEdit) -> Vec<VsEdit> {
    let mut result = Vec::new();
    let mut offset = tu(0);
    for op in edit.ops {
        match op {
            TextEditOp::Copy(range) => {
                if range.start() != offset {
                    let range = TextRange::from_to(offset, range.start());
                    result.push(VsEdit { delete: range, insert: String::new() })
                }
                offset = range.end();
            }
            TextEditOp::Insert(text) => {
                let range = TextRange::from_len(offset, tu(0));
                result.push(VsEdit { delete: range, insert: text.to_string() })
            }
        }
    }
    return result;
}
<commit_msg>style<commit_after>extern crate serde;
#[macro_use]
extern crate serde_derive;
pub extern crate neon;
pub extern crate neon_serde;
extern crate fall_tree;
pub extern crate fall_editor;

use std::iter;
use std::sync::Arc;

use neon::vm::{Call, JsResult, Lock};
use neon::mem::Handle;
use neon::js::{JsString, JsValue, JsFunction};
use neon::js::class::{Class, JsClass};

use fall_tree::{TextEditBuilder, Text, TextRange, TextEdit, TextEditOp, tu};
use fall_editor::EditorFileImpl;

mod support;

pub use self::support::{arg, ret};

pub struct EditorFile<I: EditorFileImpl> {
    imp: Arc<I>
}

impl<I: EditorFileImpl> Clone for EditorFile<I> {
    fn clone(&self) -> Self {
        EditorFile { imp: self.imp.clone() }
    }
}

impl<I: EditorFileImpl> ::std::ops::Deref for EditorFile<I> {
    type Target = I;

    fn deref(&self) -> &I {
        self.imp.deref()
    }
}

impl<I: EditorFileImpl> EditorFile<I> {
    pub fn parse(text: &str) -> Self {
        EditorFile { imp: Arc::new(I::parse(text)) }
    }
    pub fn edit(prev: &EditorFile<I>, edit: &TextEdit) -> Self {
        let imp = prev.imp.edit(edit);
        EditorFile { imp: Arc::new(imp) }
    }
}

#[macro_export]
macro_rules! declare_editor_file_class {
    ($cls:ident, $t:tt) => {
        type EditorFileHack = $crate::EditorFile<$t>;

        declare_types! {
            pub class $cls for EditorFileHack {
                init(call) {
                    use $crate::neon::js::{JsString};
                    use $crate::neon::mem::Handle;
                    use $crate::neon::vm::Lock;

                    let scope = call.scope;
                    let file = match call.arguments.len() {
                        1 => {
                            let text = call.arguments.require(scope, 0)?.check::<JsString>()?.value();
                            $crate::EditorFile::parse(&text)
                        }
                        2 => {
                            let mut file: Handle<$cls> = call.arguments.require(scope, 0)?.check::<$cls>()?;
                            let edits = call.arguments.require(scope, 1)?;
                            let edits: Vec<$crate::VsEdit> = $crate::neon_serde::from_value(scope, edits)?;
                            file.grab(|file| {
                                let inner: &$t = file;
                                let edit = $crate::from_vs_edits(
                                    <$t as $crate::fall_editor::EditorFileImpl>::file(inner).text(),
                                    edits,
                                );
                                $crate::EditorFile::edit(file, &edit)
                            })
                        }
                        _ => unreachable!()
                    };
                    Ok(file)
                }
            }
        }
    }
}

pub fn parse<C: Class>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let text = call.arguments.require(scope, 0)?.check::<JsString>()?;

    let class: Handle<JsClass<C>> = C::class(scope)?;
    let ctor: Handle<JsFunction<C>> = class.constructor(scope)?;
    let ctor_args = iter::once(text.upcast());
    let file = ctor.construct::<_, JsValue, _>(scope, ctor_args)?;
    Ok(file.upcast())
}

pub fn edit<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;

    let editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let edits = call.arguments.require(scope, 1)?;

    let class: Handle<JsClass<C>> = C::class(scope)?;
    let constructor: Handle<JsFunction<C>> = class.constructor(scope)?;
    let args = iter::once(editor_file.upcast()).chain(iter::once(edits));
    let file = constructor.construct(scope, args)?;
    Ok(file.upcast())
}

pub fn metrics<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let result = editor_file.grab(|file| file.metrics());
    ret(scope, result)
}

pub fn syntax_tree<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let result = editor_file.grab(|file| file.syntax_tree());
    ret(scope, result)
}

pub fn extend_selection<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let range: TextRange = arg(scope, &call.arguments, 1)?;
    let result = editor_file.grab(|file| file.extend_selection(range));
    ret(scope, result)
}

pub fn structure<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let result = editor_file.grab(|file| file.structure());
    ret(scope, result)
}

pub fn reformat<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let result = editor_file.grab(|file| {
        let edits = file.reformat();
        to_vs_edits(edits)
    });
    ret(scope, result)
}

pub fn highlight<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let result = editor_file.grab(|file| file.highlight());
    ret(scope, result)
}

pub fn diagnostics<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let result = editor_file.grab(|file| file.diagnostics());
    ret(scope, result)
}

pub fn context_actions<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let range: TextRange = arg(scope, &call.arguments, 1)?;
    let result = editor_file.grab(|file| file.context_actions(range));
    ret(scope, result)
}

pub fn apply_context_action<I: EditorFileImpl, C: Class<Internals=EditorFile<I>>>(call: Call) -> JsResult<JsValue> {
    let scope = call.scope;
    let mut editor_file = call.arguments.require(scope, 0)?.check::<C>()?;
    let range: TextRange = arg(scope, &call.arguments, 1)?;
    let id: String = arg(scope, &call.arguments, 2)?;
    let result = editor_file.grab(move |file| {
        file.apply_context_action(range, &id).map(to_vs_edits)
    });
    ret(scope, result)
}

#[derive(Serialize, Deserialize)]
pub struct VsEdit {
    delete: TextRange,
    insert: String,
}

pub fn from_vs_edits(text: Text, edits: Vec<VsEdit>) -> TextEdit {
    let mut edit = TextEditBuilder::new(text);
    for e in edits {
        edit.replace(e.delete, e.insert)
    }
    edit.build()
}

pub fn to_vs_edits(edit: TextEdit) -> Vec<VsEdit> {
    let mut result = Vec::new();
    let mut offset = tu(0);
    for op in edit.ops {
        match op {
            TextEditOp::Copy(range) => {
                if range.start() != offset {
                    let range = TextRange::from_to(offset, range.start());
                    result.push(VsEdit { delete: range, insert: String::new() })
                }
                offset = range.end();
            }
            TextEditOp::Insert(text) => {
                let range = TextRange::from_len(offset, tu(0));
                result.push(VsEdit { delete: range, insert: text.to_string() })
            }
        }
    }
    return result;
}
<|endoftext|>


<commit_before>#![crate_id = "seax-svm"]
#![crate_type="lib"]
#![feature(box_syntax)]

/// Contains the Seax Virtual Machine (SVM) and miscellaneous
/// support code.
pub mod svm {
    use svm::slist::List;
    use svm::slist::Stack;
    use std::iter::IteratorExt;
    use std::fmt;

    /// Singly-linked list and stack implementations. `List<T>` is a
    /// singly-linked cons list with boxed items. `Stack<T>` is basically
    /// just a struct containing a boxed pointer to the head of a list,
    /// and some methods.
    pub mod slist {

        use svm::slist::List::{Cons,Nil};
        use std::fmt;

        /// A stack implementation wrapping a `List<T>`
        ///
        /// This is essentially just a struct containing a boxed pointer
        /// to the head of a `List<T>`, so that when items are pushed to or
        /// popped from the stack, the pointer is changed to point to the
        /// new head item. There may be saner ways of doing this.
        pub struct Stack<T> {
            /// The head item of the list.
            head: Box<List<T>>
        }

        impl<T> Stack<T> {
            /// Push an item to the top of the stack, returning a new stack
            pub fn push(self, it: T) -> Stack<T> {
                Stack { head: Box::new(self.head.prepend(it)) }
            }

            /// Peak at the top item of the stack.
            ///
            /// Returns Some<T> if there is an item on top of the stack,
            /// and None if the stack is empty.
            pub fn peek(&self) -> Option<&T> {
                match *self.head {
                    Nil => None,
                    Cons(ref it,_) => Some(it)
                }
            }

            /// Returns an empty stack.
            pub fn empty() -> Stack<T> {
                Stack { head: box Nil }
            }

            /// Wraps a list into a stack.
            pub fn new(l: List<T>) -> Stack<T> {
                Stack { head: box l }
            }

            /// Returns the length of the stack. This just calls
            /// `List::length()` on the wrapped list.
            pub fn length(&self) -> isize {
                self.head.length()
            }
        }

        /// Singly-linked cons list.
        ///
        /// This is used internally to represent list primitives in the
        /// machine.
        #[deriving(Show)]
        #[deriving(PartialEq)]
        pub enum List<T> {
            Cons(T, Box<List<T>>),
            Nil,
        }

        /// Public implementation for List.
        impl<T> List<T> {

            /// Creates a new empty list
            pub fn new() -> List<T> {
                Nil
            }

            /// Prepends the given item to the list, returning the new head item.
            pub fn prepend(self, it: T) -> List<T> {
                Cons(it, box self)
            }

            /// Returns the length of the list.
            pub fn length (&self) -> isize {
                match *self {
                    Cons(_, ref tail) => 1 + tail.length(),
                    Nil => 0
                }
            }
        }

        impl<T> List<T> where T: fmt::Show {
            /// Return a string representation of the list
            fn to_string(&self) -> String {
                match *self {
                    Cons(ref head, ref tail) => format!("({:?}, {})", head, tail.to_string()),
                    Nil => format!("nil")
                }
            }
        }

        impl<T> fmt::Show for List<T> {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{}", self.to_string)
            }
        }

        /// Convenience macro for making lists.
        ///
        /// Usage: `list!(1i32, 2i32, 3i32);` expands to
        /// `Cons(1i32, Box::new(Cons(2i32, Box::new(Cons(3i32, Box::new(Nil))))));`.
        macro_rules! list(
            ( $e:expr, $($rest:expr),+ ) => ( Cons($e, Box::new(list!( $( $rest ),+ )) ));
            ( $e:expr ) => ( Cons($e, Box::new(Nil)) );
            () => ( @Empty )
        );

        #[cfg(test)]
        mod tests {
            use super::{List, Stack};
            use super::List::{Cons,Nil};

            #[test]
            fn test_list_length() {
                let full_list: List<i32> = list!(1i32, 2i32, 3i32);
                let empty_list: List<i32> = List::new();
                assert_eq!(full_list.length(), 3);
                assert_eq!(empty_list.length(), 0);
            }

            #[test]
            fn test_list_to_string() {
                let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))");
            }

            #[test]
            fn test_stack_length() {
                let full_stack: Stack<i32> = Stack::new(list!(1i32, 2i32, 3i32));
                let empty_stack: Stack<i32> = Stack::empty();
                assert_eq!(full_stack.length(), 3);
                assert_eq!(empty_stack.length(), 0);
            }

            #[test]
            fn test_stack_peek() {
                let full_stack: Stack<i32> = Stack::new(list!(1i32, 2i32, 3i32));
                let empty_stack: Stack<i32> = Stack::empty();
                assert_eq!(full_stack.peek(), Some(&1));
                assert_eq!(empty_stack.peek(), None);
            }

            #[test]
            fn test_stack_push() {
                let mut s: Stack<i32> = Stack::empty();
                assert_eq!(s.peek(), None);
                s = s.push(1);
                assert_eq!(s.peek(), Some(&1));
                s = s.push(6);
                assert_eq!(s.peek(), Some(&6));
            }

            #[test]
            fn test_stack_pop() {
                let mut s: Stack<i32> = Stack::empty();
                assert_eq!(s.peek(), None); // TODO: implement
                s = s.push(1);
                assert_eq!(s.peek(), Some(&1));
                s = s.push(6);
                assert_eq!(s.peek(), Some(&6));
            }

            #[test]
            fn test_list_macro() {
                let l: List<i32> = list!(1i32, 2i32, 3i32);
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))")
            }
        }
    }

    /// SVM item types
    #[deriving(Show)]
    #[deriving(PartialEq)]
    pub enum SVMCell {
        Atom,
        ListCell(Box<List<SVMCell>>)
    }

    pub enum Atom {
        AtomUInt(usize),
        AtomSInt(isize),
        AtomFloat(f64),
        AtomStr(String)
    }

    /// SVM instruction types
    pub enum SVMInstruction {
        /// `nil`
        ///
        /// Pushes an empty list (nil) onto the stack
        InstNIL,
        /// `ldc`: `L`oa`d` `C`onstant. Loads a constant (atom)
        InstLDC(Atom),
        /// `ld`: `L`oa`d`. Pushes a variable onto the stack.
        ///
        /// The variable is indicated by the argument, a pair.
        /// The pair's `car` specifies the level, the `cdr` the position.
        /// So `(1 . 3)` gives the current function's (level 1) third
        /// parameter.
        InstLD,
        /// `ldf`: `L`oa`d` `F`unction.
        ///
        ///  Takes one list argument representing a function and constructs
        ///  a closure (a pair containing the function and the current
        ///  environment) and pushes that onto the stack.
        InstLDF,
        /// `join`
        ///
        /// Pops a list reference from the dump and makes this the new value
        /// of `C`. This instruction occurs at the end of both alternatives of
        ///  a `sel`.
        InstJOIN,
        /// `ap`: `Ap`ply.
        ///
        /// Pops a closure and a list of parameter values from the stack.
        /// The closure is applied to the parameters by installing its
        /// environment as the current one, pushing the parameter list
        /// in front of that, clearing the stack, and setting `C` to the
        /// closure's function pointer. The previous values of `S`, `E`,
        ///  and the next value of `C` are saved on the dump.
        InstAP,
        /// `ret`: `Ret`urn.
        ///
        /// Pops one return value from the stack, restores
        /// `S`, `E`, and `C` from the dump, and pushes
        /// the return value onto the now-current stack.
        InstRET,
        /// `dum`: `Dum`my.
        ///
        /// Pops a dummy environment (an empty list) onto the `E` stack.
        InstDUM,
        /// `rap`: `R`ecursive `Ap`ply.
        /// Works like `ap`, only that it replaces an occurrence of a
        /// dummy environment with the current one, thus making recursive
        ///  functions possible.
        InstRAP,
        /// `sel`: `Sel`ect
        ///
        /// Expects two list arguments, and pops a value from the stack.
        /// The first list is executed if the popped value was non-nil,
        /// the second list otherwise. Before one of these list pointers
        ///  is made the new `C`, a pointer to the instruction following
        ///  `sel` is saved on the dump.
        InstSEL,
        /// `add`
        ///
        /// Pops two numbers off of the stack and adds them, pushing the
        /// result onto the stack. This will up-convert integers to floating
        /// point if necessary.
        ///
        /// TODO: figure out what happens when you try to add things that aren't
        /// numbers (maybe the compiler won't let this happen?).
        InstADD,
        /// `sub`: `Sub`tract
        ///
        /// Pops two numbers off of the stack and subtracts the first from the
        /// second, pushing the result onto the stack. This will up-convert
        /// integers to floating point if necessary.
        ///
        /// TODO: figure out what happens when you try to subtract things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstSUB,
        /// `mul`: `Mul`tiply
        ///
        /// Pops two numbers off of the stack and multiplies them, pushing the
        /// result onto the stack. This will up-convert integers to floating
        /// point if necessary.
        ///
        /// TODO: figure out what happens when you try to multiply things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstMUL,
        /// `div`: `Div`ide
        ///
        /// Pops two numbers off of the stack and divides the first by the second,
        /// pushing the result onto the stack. This performs integer division.
        ///
        /// TODO: figure out what happens when you try to divide things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstDIV,
        /// `fdiv`: `F`loating-point `div`ide
        ///
        /// Pops two numbers off of the stack and divides the first by the second,
        /// pushing the result onto the stack. This performs float division.
        ///
        /// TODO: figure out what happens when you try to divide things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        ///
        /// TODO: Not sure if there should be separate float and int divide words
        /// I guess the compiler can figure this out
        InstFDIV,
        /// `mod`: `Mod`ulo
        ///
        /// Pops two numbers off of the stack and divides the first by the second,
        /// pushing the remainder onto the stack.
        ///
        /// TODO: figure out what happens when you try to modulo things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstMOD
        // TODO: add some hardcoded I/O instructions here so that you can
        //  do I/O without farming everything out to `stdio`
        // TODO: add `cons` and `cdr` words
    }

    /// Represents a SVM machine state
    pub struct State {
        stack: Stack<SVMCell>,
        env: Stack<SVMCell>,
        control: Stack<SVMCell>,
        dump: Stack<SVMCell>
    }

    impl State {

        /// Creates a new empty state
        fn new() -> State {
            State {
                stack: Stack::empty(),
                env: Stack::empty(),
                control: Stack::empty(),
                dump: Stack::empty()
            }
        }

        /// Evaluates an instruction.
        ///
        /// Evaluates an instruction against a state, returning a new state.
        pub fn eval(self, inst: SVMInstruction) -> State {
            match inst {
                SVMInstruction::InstNIL => State {
                    stack: self.stack.push(SVMCell::ListCell(box List::new())),
                    env: self.env,
                    control: self.control,
                    dump: self.dump
                },
                _ => { unimplemented!() }
            }
        }
    }

    /*
    /// Evaluates a program.
    ///
    /// Evaluates a program represented as an `Iterator` of `SVMInstruction`s.
    /// Returns the final machine state at the end of execution

    pub fn evalProgram(insts: Iterator<Item=SVMInstruction>) -> State {
        insts.fold(State::new(), |last_state: State, inst: SVMInstruction| last_state.eval(inst));
    }*/

    #[cfg(test)]
    mod tests {
        use super::State;
        use super::SVMInstruction;
        use super::slist::List::{Cons,Nil};

        #[test]
        fn test_empty_state() {
            let state = State::new();
            assert_eq!(state.stack.length(), 0);
            assert_eq!(state.env.length(), 0);
            assert_eq!(state.control.length(), 0);
            assert_eq!(state.dump.length(), 0);
        }

        #[test]
        fn test_eval_nil () {
            let mut state = State::new();
            assert_eq!(state.stack.peek(), None);
            state = state.eval(SVMInstruction::InstNIL);
            assert_eq!(state.stack.peek(), Some(&Nil));
        }
    }

}
<commit_msg>Add additional State tests, implement Show and PartialEq for types<commit_after>#![crate_id = "seax-svm"]
#![crate_type="lib"]
#![feature(box_syntax)]

/// Contains the Seax Virtual Machine (SVM) and miscellaneous
/// support code.
pub mod svm {
    use svm::slist::List;
    use svm::slist::Stack;
    use std::iter::IteratorExt;
    use std::fmt;

    /// Singly-linked list and stack implementations. `List<T>` is a
    /// singly-linked cons list with boxed items. `Stack<T>` is basically
    /// just a struct containing a boxed pointer to the head of a list,
    /// and some methods.
    pub mod slist {

        use svm::slist::List::{Cons,Nil};
        use std::fmt;

        /// A stack implementation wrapping a `List<T>`
        ///
        /// This is essentially just a struct containing a boxed pointer
        /// to the head of a `List<T>`, so that when items are pushed to or
        /// popped from the stack, the pointer is changed to point to the
        /// new head item. There may be saner ways of doing this.
        pub struct Stack<T> {
            /// The head item of the list.
            head: Box<List<T>>
        }

        impl<T> Stack<T> {
            /// Push an item to the top of the stack, returning a new stack
            pub fn push(self, it: T) -> Stack<T> {
                Stack { head: Box::new(self.head.prepend(it)) }
            }

            /// Peak at the top item of the stack.
            ///
            /// Returns Some<T> if there is an item on top of the stack,
            /// and None if the stack is empty.
            pub fn peek(&self) -> Option<&T> {
                match *self.head {
                    Nil => None,
                    Cons(ref it,_) => Some(it)
                }
            }

            /// Returns an empty stack.
            pub fn empty() -> Stack<T> {
                Stack { head: box Nil }
            }

            /// Wraps a list into a stack.
            pub fn new(l: List<T>) -> Stack<T> {
                Stack { head: box l }
            }

            /// Returns the length of the stack. This just calls
            /// `List::length()` on the wrapped list.
            pub fn length(&self) -> isize {
                self.head.length()
            }
        }

        /// Singly-linked cons list.
        ///
        /// This is used internally to represent list primitives in the
        /// machine.
        #[derive(PartialEq)]
        pub enum List<T> {
            Cons(T, Box<List<T>>),
            Nil,
        }

        /// Public implementation for List.
        impl<T> List<T> {

            /// Creates a new empty list
            pub fn new() -> List<T> {
                Nil
            }

            /// Prepends the given item to the list, returning the new head item.
            pub fn prepend(self, it: T) -> List<T> {
                Cons(it, box self)
            }

            /// Returns the length of the list.
            pub fn length (&self) -> isize {
                match *self {
                    Cons(_, ref tail) => 1 + tail.length(),
                    Nil => 0
                }
            }
        }

        impl<T> List<T> where T: fmt::Show {
            /// Return a string representation of the list
            fn to_string(&self) -> String {
                match *self {
                    Cons(ref head, ref tail) => format!("({:?}, {})", head, tail.to_string()),
                    Nil => format!("nil")
                }
            }
        }

        impl<T> fmt::Show for List<T> where T: fmt::Show {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "{}", self.to_string())
            }
        }

        /// Convenience macro for making lists.
        ///
        /// Usage: `list!(1i32, 2i32, 3i32);` expands to
        /// `Cons(1i32, Box::new(Cons(2i32, Box::new(Cons(3i32, Box::new(Nil))))));`.
        macro_rules! list(
            ( $e:expr, $($rest:expr),+ ) => ( Cons($e, Box::new(list!( $( $rest ),+ )) ));
            ( $e:expr ) => ( Cons($e, Box::new(Nil)) );
            () => ( @Empty )
        );

        #[cfg(test)]
        mod tests {
            use super::{List, Stack};
            use super::List::{Cons,Nil};

            #[test]
            fn test_list_length() {
                let full_list: List<i32> = list!(1i32, 2i32, 3i32);
                let empty_list: List<i32> = List::new();
                assert_eq!(full_list.length(), 3);
                assert_eq!(empty_list.length(), 0);
            }

            #[test]
            fn test_list_to_string() {
                let l: List<i32> = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))");
            }

            #[test]
            fn test_stack_length() {
                let full_stack: Stack<i32> = Stack::new(list!(1i32, 2i32, 3i32));
                let empty_stack: Stack<i32> = Stack::empty();
                assert_eq!(full_stack.length(), 3);
                assert_eq!(empty_stack.length(), 0);
            }

            #[test]
            fn test_stack_peek() {
                let full_stack: Stack<i32> = Stack::new(list!(1i32, 2i32, 3i32));
                let empty_stack: Stack<i32> = Stack::empty();
                assert_eq!(full_stack.peek(), Some(&1));
                assert_eq!(empty_stack.peek(), None);
            }

            #[test]
            fn test_stack_push() {
                let mut s: Stack<i32> = Stack::empty();
                assert_eq!(s.peek(), None);
                s = s.push(1);
                assert_eq!(s.peek(), Some(&1));
                s = s.push(6);
                assert_eq!(s.peek(), Some(&6));
            }

            #[test]
            fn test_stack_pop() {
                let mut s: Stack<i32> = Stack::empty();
                assert_eq!(s.peek(), None); // TODO: implement
                s = s.push(1);
                assert_eq!(s.peek(), Some(&1));
                s = s.push(6);
                assert_eq!(s.peek(), Some(&6));
            }

            #[test]
            fn test_list_macro() {
                let l: List<i32> = list!(1i32, 2i32, 3i32);
                assert_eq!(l.to_string(), "(1i32, (2i32, (3i32, nil)))")
            }
        }
    }

    /// SVM item types
    #[derive(PartialEq)]
    pub enum SVMCell {
        Atom,
        ListCell(Box<List<SVMCell>>)
    }

    impl fmt::Show for SVMCell {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "[{:?}]", self)
        }
    }

    pub enum Atom {
        UInt(usize),
        SInt(isize),
        Float(f64),
        Str(String)
    }

    impl fmt::Show for Atom {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            match self {
                &Atom::UInt(value) => write!(f, "{}u", value),
                &Atom::SInt(value) => write!(f, "{}i", value),
                &Atom::Float(value) => write!(f, "{}f", value),
                &Atom::Str(ref value) => write!(f, "\"{}\"", value)
            }
        }
    }

    /// SVM instruction types
    pub enum SVMInstruction {
        /// `nil`
        ///
        /// Pushes an empty list (nil) onto the stack
        InstNIL,
        /// `ldc`: `L`oa`d` `C`onstant. Loads a constant (atom)
        InstLDC(Atom),
        /// `ld`: `L`oa`d`. Pushes a variable onto the stack.
        ///
        /// The variable is indicated by the argument, a pair.
        /// The pair's `car` specifies the level, the `cdr` the position.
        /// So `(1 . 3)` gives the current function's (level 1) third
        /// parameter.
        InstLD,
        /// `ldf`: `L`oa`d` `F`unction.
        ///
        ///  Takes one list argument representing a function and constructs
        ///  a closure (a pair containing the function and the current
        ///  environment) and pushes that onto the stack.
        InstLDF,
        /// `join`
        ///
        /// Pops a list reference from the dump and makes this the new value
        /// of `C`. This instruction occurs at the end of both alternatives of
        ///  a `sel`.
        InstJOIN,
        /// `ap`: `Ap`ply.
        ///
        /// Pops a closure and a list of parameter values from the stack.
        /// The closure is applied to the parameters by installing its
        /// environment as the current one, pushing the parameter list
        /// in front of that, clearing the stack, and setting `C` to the
        /// closure's function pointer. The previous values of `S`, `E`,
        ///  and the next value of `C` are saved on the dump.
        InstAP,
        /// `ret`: `Ret`urn.
        ///
        /// Pops one return value from the stack, restores
        /// `S`, `E`, and `C` from the dump, and pushes
        /// the return value onto the now-current stack.
        InstRET,
        /// `dum`: `Dum`my.
        ///
        /// Pops a dummy environment (an empty list) onto the `E` stack.
        InstDUM,
        /// `rap`: `R`ecursive `Ap`ply.
        /// Works like `ap`, only that it replaces an occurrence of a
        /// dummy environment with the current one, thus making recursive
        ///  functions possible.
        InstRAP,
        /// `sel`: `Sel`ect
        ///
        /// Expects two list arguments, and pops a value from the stack.
        /// The first list is executed if the popped value was non-nil,
        /// the second list otherwise. Before one of these list pointers
        ///  is made the new `C`, a pointer to the instruction following
        ///  `sel` is saved on the dump.
        InstSEL,
        /// `add`
        ///
        /// Pops two numbers off of the stack and adds them, pushing the
        /// result onto the stack. This will up-convert integers to floating
        /// point if necessary.
        ///
        /// TODO: figure out what happens when you try to add things that aren't
        /// numbers (maybe the compiler won't let this happen?).
        InstADD,
        /// `sub`: `Sub`tract
        ///
        /// Pops two numbers off of the stack and subtracts the first from the
        /// second, pushing the result onto the stack. This will up-convert
        /// integers to floating point if necessary.
        ///
        /// TODO: figure out what happens when you try to subtract things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstSUB,
        /// `mul`: `Mul`tiply
        ///
        /// Pops two numbers off of the stack and multiplies them, pushing the
        /// result onto the stack. This will up-convert integers to floating
        /// point if necessary.
        ///
        /// TODO: figure out what happens when you try to multiply things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstMUL,
        /// `div`: `Div`ide
        ///
        /// Pops two numbers off of the stack and divides the first by the second,
        /// pushing the result onto the stack. This performs integer division.
        ///
        /// TODO: figure out what happens when you try to divide things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstDIV,
        /// `fdiv`: `F`loating-point `div`ide
        ///
        /// Pops two numbers off of the stack and divides the first by the second,
        /// pushing the result onto the stack. This performs float division.
        ///
        /// TODO: figure out what happens when you try to divide things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        ///
        /// TODO: Not sure if there should be separate float and int divide words
        /// I guess the compiler can figure this out
        InstFDIV,
        /// `mod`: `Mod`ulo
        ///
        /// Pops two numbers off of the stack and divides the first by the second,
        /// pushing the remainder onto the stack.
        ///
        /// TODO: figure out what happens when you try to modulo things that
        /// aren't numbers (maybe the compiler won't let this happen?).
        InstMOD
        // TODO: add some hardcoded I/O instructions here so that you can
        //  do I/O without farming everything out to `stdio`
        // TODO: add `cons` and `cdr` words
    }

    /// Represents a SVM machine state
    pub struct State {
        stack: Stack<SVMCell>,
        env: Stack<SVMCell>,
        control: Stack<SVMCell>,
        dump: Stack<SVMCell>
    }

    impl State {

        /// Creates a new empty state
        fn new() -> State {
            State {
                stack: Stack::empty(),
                env: Stack::empty(),
                control: Stack::empty(),
                dump: Stack::empty()
            }
        }

        /// Evaluates an instruction.
        ///
        /// Evaluates an instruction against a state, returning a new state.
        pub fn eval(self, inst: SVMInstruction) -> State {
            match inst {
                SVMInstruction::InstNIL => State {
                    stack: self.stack.push(SVMCell::ListCell(box List::new())),
                    env: self.env,
                    control: self.control,
                    dump: self.dump
                },
                _ => { unimplemented!() }
            }
        }
    }

    /*
    /// Evaluates a program.
    ///
    /// Evaluates a program represented as an `Iterator` of `SVMInstruction`s.
    /// Returns the final machine state at the end of execution

    pub fn evalProgram(insts: Iterator<Item=SVMInstruction>) -> State {
        insts.fold(State::new(), |last_state: State, inst: SVMInstruction| last_state.eval(inst));
    }*/

    #[cfg(test)]
    mod tests {
        use super::State;
        use super::{SVMInstruction, SVMCell};
        use super::slist::List::{Cons,Nil};

        #[test]
        fn test_empty_state() {
            let state = State::new();
            assert_eq!(state.stack.length(), 0);
            assert_eq!(state.env.length(), 0);
            assert_eq!(state.control.length(), 0);
            assert_eq!(state.dump.length(), 0);
        }

        #[test]
        fn test_eval_nil () {
            let mut state = State::new();
            assert_eq!(state.stack.peek(), None);
            state = state.eval(SVMInstruction::InstNIL);
            assert_eq!(state.stack.peek(), Some(&SVMCell::ListCell(box Nil)));
        }
    }

}
<|endoftext|>


<commit_before>//! This module handles auto-magic editing actions applied together with users
//! edits. For example, if the user typed
//!
//! ```text
//!     foo
//!         .bar()
//!         .baz()
//!     |   // <- cursor is here
//! ```
//!
//! and types `.` next, we want to indent the dot.
//!
//! Language server executes such typing assists synchronously. That is, they
//! block user's typing and should be pretty fast for this reason!

use ra_db::{FilePosition, SourceDatabase};
use ra_fmt::leading_indent;
use ra_ide_db::RootDatabase;
use ra_syntax::{
    algo::find_node_at_offset,
    ast::{self, AstToken},
    AstNode, SmolStr, SourceFile,
    SyntaxKind::*,
    SyntaxToken, TextRange, TextUnit, TokenAtOffset,
};
use ra_text_edit::TextEdit;

use crate::{source_change::SingleFileChange, SourceChange, SourceFileEdit};

pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
    let parse = db.parse(position.file_id);
    let file = parse.tree();
    let comment = file
        .syntax()
        .token_at_offset(position.offset)
        .left_biased()
        .and_then(ast::Comment::cast)?;

    if comment.kind().shape.is_block() {
        return None;
    }

    let prefix = comment.prefix();
    let comment_range = comment.syntax().text_range();
    if position.offset < comment_range.start() + TextUnit::of_str(prefix) + TextUnit::from(1) {
        return None;
    }

    // Continuing non-doc line comments (like this one :) ) is annoying
    if prefix == "//" && comment_range.end() == position.offset {
        return None;
    }

    let indent = node_indent(&file, comment.syntax())?;
    let inserted = format!("\n{}{} ", indent, prefix);
    let cursor_position = position.offset + TextUnit::of_str(&inserted);
    let edit = TextEdit::insert(position.offset, inserted);

    Some(
        SourceChange::source_file_edit(
            "on enter",
            SourceFileEdit { edit, file_id: position.file_id },
        )
        .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }),
    )
}

fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
    let ws = match file.syntax().token_at_offset(token.text_range().start()) {
        TokenAtOffset::Between(l, r) => {
            assert!(r == *token);
            l
        }
        TokenAtOffset::Single(n) => {
            assert!(n == *token);
            return Some("".into());
        }
        TokenAtOffset::None => unreachable!(),
    };
    if ws.kind() != WHITESPACE {
        return None;
    }
    let text = ws.text();
    let pos = text.rfind('\n').map(|it| it + 1).unwrap_or(0);
    Some(text[pos..].into())
}

pub(crate) const TRIGGER_CHARS: &str = ".=>";

pub(crate) fn on_char_typed(
    db: &RootDatabase,
    position: FilePosition,
    char_typed: char,
) -> Option<SourceChange> {
    assert!(TRIGGER_CHARS.contains(char_typed));
    let file = &db.parse(position.file_id).tree();
    assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed));
    let single_file_change = on_char_typed_inner(file, position.offset, char_typed)?;
    Some(single_file_change.into_source_change(position.file_id))
}

fn on_char_typed_inner(
    file: &SourceFile,
    offset: TextUnit,
    char_typed: char,
) -> Option<SingleFileChange> {
    assert!(TRIGGER_CHARS.contains(char_typed));
    match char_typed {
        '.' => on_dot_typed(file, offset),
        '=' => on_eq_typed(file, offset),
        '>' => on_arrow_typed(file, offset),
        _ => unreachable!(),
    }
}

/// Returns an edit which should be applied after `=` was typed. Primarily,
/// this works when adding `let =`.
// FIXME: use a snippet completion instead of this hack here.
fn on_eq_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
    assert_eq!(file.syntax().text().char_at(offset), Some('='));
    let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
    if let_stmt.has_semi() {
        return None;
    }
    if let Some(expr) = let_stmt.initializer() {
        let expr_range = expr.syntax().text_range();
        if expr_range.contains(offset) && offset != expr_range.start() {
            return None;
        }
        if file.syntax().text().slice(offset..expr_range.start()).contains_char('\n') {
            return None;
        }
    } else {
        return None;
    }
    let offset = let_stmt.syntax().text_range().end();
    Some(SingleFileChange {
        label: "add semicolon".to_string(),
        edit: TextEdit::insert(offset, ";".to_string()),
        cursor_position: None,
    })
}

/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
fn on_dot_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
    assert_eq!(file.syntax().text().char_at(offset), Some('.'));
    let whitespace =
        file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;

    let current_indent = {
        let text = whitespace.text();
        let newline = text.rfind('\n')?;
        &text[newline + 1..]
    };
    let current_indent_len = TextUnit::of_str(current_indent);

    // Make sure dot is a part of call chain
    let field_expr = ast::FieldExpr::cast(whitespace.syntax().parent())?;
    let prev_indent = leading_indent(field_expr.syntax())?;
    let target_indent = format!("    {}", prev_indent);
    let target_indent_len = TextUnit::of_str(&target_indent);
    if current_indent_len == target_indent_len {
        return None;
    }

    Some(SingleFileChange {
        label: "reindent dot".to_string(),
        edit: TextEdit::replace(
            TextRange::from_to(offset - current_indent_len, offset),
            target_indent,
        ),
        cursor_position: Some(
            offset + target_indent_len - current_indent_len + TextUnit::of_char('.'),
        ),
    })
}

/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
fn on_arrow_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
    let file_text = file.syntax().text();
    assert_eq!(file_text.char_at(offset), Some('>'));
    let after_arrow = offset + TextUnit::of_char('>');
    if file_text.char_at(after_arrow) != Some('{') {
        return None;
    }
    if find_node_at_offset::<ast::RetType>(file.syntax(), offset).is_none() {
        return None;
    }

    Some(SingleFileChange {
        label: "add space after return type".to_string(),
        edit: TextEdit::insert(after_arrow, " ".to_string()),
        cursor_position: Some(after_arrow),
    })
}

#[cfg(test)]
mod tests {
    use test_utils::{add_cursor, assert_eq_text, extract_offset};

    use crate::mock_analysis::single_file;

    use super::*;

    #[test]
    fn test_on_enter() {
        fn apply_on_enter(before: &str) -> Option<String> {
            let (offset, before) = extract_offset(before);
            let (analysis, file_id) = single_file(&before);
            let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?;

            assert_eq!(result.source_file_edits.len(), 1);
            let actual = result.source_file_edits[0].edit.apply(&before);
            let actual = add_cursor(&actual, result.cursor_position.unwrap().offset);
            Some(actual)
        }

        fn do_check(before: &str, after: &str) {
            let actual = apply_on_enter(before).unwrap();
            assert_eq_text!(after, &actual);
        }

        fn do_check_noop(text: &str) {
            assert!(apply_on_enter(text).is_none())
        }

        do_check(
            r"
/// Some docs<|>
fn foo() {
}
",
            r"
/// Some docs
/// <|>
fn foo() {
}
",
        );
        do_check(
            r"
impl S {
    /// Some<|> docs.
    fn foo() {}
}
",
            r"
impl S {
    /// Some
    /// <|> docs.
    fn foo() {}
}
",
        );
        do_check(
            r"
fn main() {
    // Fix<|> me
    let x = 1 + 1;
}
",
            r"
fn main() {
    // Fix
    // <|> me
    let x = 1 + 1;
}
",
        );
        do_check_noop(
            r"
fn main() {
    // Fix me<|>
    let x = 1 + 1;
}
",
        );

        do_check_noop(r"<|>//! docz");
    }

    fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> {
        let (offset, before) = extract_offset(before);
        let edit = TextEdit::insert(offset, char_typed.to_string());
        let before = edit.apply(&before);
        let parse = SourceFile::parse(&before);
        on_char_typed_inner(&parse.tree(), offset, char_typed)
            .map(|it| (it.edit.apply(&before), it))
    }

    fn type_char(char_typed: char, before: &str, after: &str) {
        let (actual, file_change) = do_type_char(char_typed, before)
            .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));

        if after.contains("<|>") {
            let (offset, after) = extract_offset(after);
            assert_eq_text!(&after, &actual);
            assert_eq!(file_change.cursor_position, Some(offset))
        } else {
            assert_eq_text!(after, &actual);
        }
    }

    fn type_char_noop(char_typed: char, before: &str) {
        let file_change = do_type_char(char_typed, before);
        assert!(file_change.is_none())
    }

    #[test]
    fn test_on_eq_typed() {
        //     do_check(r"
        // fn foo() {
        //     let foo =<|>
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        // }
        // ");
        type_char(
            '=',
            r"
fn foo() {
    let foo <|> 1 + 1
}
",
            r"
fn foo() {
    let foo = 1 + 1;
}
",
        );
        //     do_check(r"
        // fn foo() {
        //     let foo =<|>
        //     let bar = 1;
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        //     let bar = 1;
        // }
        // ");
    }

    #[test]
    fn indents_new_chain_call() {
        type_char(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    <|>
            }
            ",
        )
    }

    #[test]
    fn indents_new_chain_call_with_semi() {
        type_char(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                <|>;
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .;
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    <|>;
            }
            ",
        )
    }

    #[test]
    fn indents_continued_chain_call() {
        type_char(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    .
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    <|>
            }
            ",
        );
    }

    #[test]
    fn indents_middle_of_chain_call() {
        type_char(
            '.',
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                <|>
                    .nth(92)
                    .unwrap();
            }
            ",
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                    .
                    .nth(92)
                    .unwrap();
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                    <|>
                    .nth(92)
                    .unwrap();
            }
            ",
        );
    }

    #[test]
    fn dont_indent_freestanding_dot() {
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                <|>
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
            <|>
            }
            ",
        );
    }

    #[test]
    fn adds_space_after_return_type() {
        type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -><|> { 92 }")
    }
}
<commit_msg>Fix off by one in onEnter<commit_after>//! This module handles auto-magic editing actions applied together with users
//! edits. For example, if the user typed
//!
//! ```text
//!     foo
//!         .bar()
//!         .baz()
//!     |   // <- cursor is here
//! ```
//!
//! and types `.` next, we want to indent the dot.
//!
//! Language server executes such typing assists synchronously. That is, they
//! block user's typing and should be pretty fast for this reason!

use ra_db::{FilePosition, SourceDatabase};
use ra_fmt::leading_indent;
use ra_ide_db::RootDatabase;
use ra_syntax::{
    algo::find_node_at_offset,
    ast::{self, AstToken},
    AstNode, SmolStr, SourceFile,
    SyntaxKind::*,
    SyntaxToken, TextRange, TextUnit, TokenAtOffset,
};
use ra_text_edit::TextEdit;

use crate::{source_change::SingleFileChange, SourceChange, SourceFileEdit};

pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<SourceChange> {
    let parse = db.parse(position.file_id);
    let file = parse.tree();
    let comment = file
        .syntax()
        .token_at_offset(position.offset)
        .left_biased()
        .and_then(ast::Comment::cast)?;

    if comment.kind().shape.is_block() {
        return None;
    }

    let prefix = comment.prefix();
    let comment_range = comment.syntax().text_range();
    if position.offset < comment_range.start() + TextUnit::of_str(prefix) {
        return None;
    }

    // Continuing non-doc line comments (like this one :) ) is annoying
    if prefix == "//" && comment_range.end() == position.offset {
        return None;
    }

    let indent = node_indent(&file, comment.syntax())?;
    let inserted = format!("\n{}{} ", indent, prefix);
    let cursor_position = position.offset + TextUnit::of_str(&inserted);
    let edit = TextEdit::insert(position.offset, inserted);

    Some(
        SourceChange::source_file_edit(
            "on enter",
            SourceFileEdit { edit, file_id: position.file_id },
        )
        .with_cursor(FilePosition { offset: cursor_position, file_id: position.file_id }),
    )
}

fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> {
    let ws = match file.syntax().token_at_offset(token.text_range().start()) {
        TokenAtOffset::Between(l, r) => {
            assert!(r == *token);
            l
        }
        TokenAtOffset::Single(n) => {
            assert!(n == *token);
            return Some("".into());
        }
        TokenAtOffset::None => unreachable!(),
    };
    if ws.kind() != WHITESPACE {
        return None;
    }
    let text = ws.text();
    let pos = text.rfind('\n').map(|it| it + 1).unwrap_or(0);
    Some(text[pos..].into())
}

pub(crate) const TRIGGER_CHARS: &str = ".=>";

pub(crate) fn on_char_typed(
    db: &RootDatabase,
    position: FilePosition,
    char_typed: char,
) -> Option<SourceChange> {
    assert!(TRIGGER_CHARS.contains(char_typed));
    let file = &db.parse(position.file_id).tree();
    assert_eq!(file.syntax().text().char_at(position.offset), Some(char_typed));
    let single_file_change = on_char_typed_inner(file, position.offset, char_typed)?;
    Some(single_file_change.into_source_change(position.file_id))
}

fn on_char_typed_inner(
    file: &SourceFile,
    offset: TextUnit,
    char_typed: char,
) -> Option<SingleFileChange> {
    assert!(TRIGGER_CHARS.contains(char_typed));
    match char_typed {
        '.' => on_dot_typed(file, offset),
        '=' => on_eq_typed(file, offset),
        '>' => on_arrow_typed(file, offset),
        _ => unreachable!(),
    }
}

/// Returns an edit which should be applied after `=` was typed. Primarily,
/// this works when adding `let =`.
// FIXME: use a snippet completion instead of this hack here.
fn on_eq_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
    assert_eq!(file.syntax().text().char_at(offset), Some('='));
    let let_stmt: ast::LetStmt = find_node_at_offset(file.syntax(), offset)?;
    if let_stmt.has_semi() {
        return None;
    }
    if let Some(expr) = let_stmt.initializer() {
        let expr_range = expr.syntax().text_range();
        if expr_range.contains(offset) && offset != expr_range.start() {
            return None;
        }
        if file.syntax().text().slice(offset..expr_range.start()).contains_char('\n') {
            return None;
        }
    } else {
        return None;
    }
    let offset = let_stmt.syntax().text_range().end();
    Some(SingleFileChange {
        label: "add semicolon".to_string(),
        edit: TextEdit::insert(offset, ";".to_string()),
        cursor_position: None,
    })
}

/// Returns an edit which should be applied when a dot ('.') is typed on a blank line, indenting the line appropriately.
fn on_dot_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
    assert_eq!(file.syntax().text().char_at(offset), Some('.'));
    let whitespace =
        file.syntax().token_at_offset(offset).left_biased().and_then(ast::Whitespace::cast)?;

    let current_indent = {
        let text = whitespace.text();
        let newline = text.rfind('\n')?;
        &text[newline + 1..]
    };
    let current_indent_len = TextUnit::of_str(current_indent);

    // Make sure dot is a part of call chain
    let field_expr = ast::FieldExpr::cast(whitespace.syntax().parent())?;
    let prev_indent = leading_indent(field_expr.syntax())?;
    let target_indent = format!("    {}", prev_indent);
    let target_indent_len = TextUnit::of_str(&target_indent);
    if current_indent_len == target_indent_len {
        return None;
    }

    Some(SingleFileChange {
        label: "reindent dot".to_string(),
        edit: TextEdit::replace(
            TextRange::from_to(offset - current_indent_len, offset),
            target_indent,
        ),
        cursor_position: Some(
            offset + target_indent_len - current_indent_len + TextUnit::of_char('.'),
        ),
    })
}

/// Adds a space after an arrow when `fn foo() { ... }` is turned into `fn foo() -> { ... }`
fn on_arrow_typed(file: &SourceFile, offset: TextUnit) -> Option<SingleFileChange> {
    let file_text = file.syntax().text();
    assert_eq!(file_text.char_at(offset), Some('>'));
    let after_arrow = offset + TextUnit::of_char('>');
    if file_text.char_at(after_arrow) != Some('{') {
        return None;
    }
    if find_node_at_offset::<ast::RetType>(file.syntax(), offset).is_none() {
        return None;
    }

    Some(SingleFileChange {
        label: "add space after return type".to_string(),
        edit: TextEdit::insert(after_arrow, " ".to_string()),
        cursor_position: Some(after_arrow),
    })
}

#[cfg(test)]
mod tests {
    use test_utils::{add_cursor, assert_eq_text, extract_offset};

    use crate::mock_analysis::single_file;

    use super::*;

    #[test]
    fn test_on_enter() {
        fn apply_on_enter(before: &str) -> Option<String> {
            let (offset, before) = extract_offset(before);
            let (analysis, file_id) = single_file(&before);
            let result = analysis.on_enter(FilePosition { offset, file_id }).unwrap()?;

            assert_eq!(result.source_file_edits.len(), 1);
            let actual = result.source_file_edits[0].edit.apply(&before);
            let actual = add_cursor(&actual, result.cursor_position.unwrap().offset);
            Some(actual)
        }

        fn do_check(before: &str, after: &str) {
            let actual = apply_on_enter(before).unwrap();
            assert_eq_text!(after, &actual);
        }

        fn do_check_noop(text: &str) {
            assert!(apply_on_enter(text).is_none())
        }

        do_check(
            r"
/// Some docs<|>
fn foo() {
}
",
            r"
/// Some docs
/// <|>
fn foo() {
}
",
        );
        do_check(
            r"
impl S {
    /// Some<|> docs.
    fn foo() {}
}
",
            r"
impl S {
    /// Some
    /// <|> docs.
    fn foo() {}
}
",
        );
        do_check(
            r"
fn main() {
    // Fix<|> me
    let x = 1 + 1;
}
",
            r"
fn main() {
    // Fix
    // <|> me
    let x = 1 + 1;
}
",
        );
        do_check(
            r"
///<|> Some docs
fn foo() {
}
",
            r"
///
/// <|> Some docs
fn foo() {
}
",
        );
        do_check_noop(
            r"
fn main() {
    // Fix me<|>
    let x = 1 + 1;
}
",
        );

        do_check_noop(r"<|>//! docz");
    }

    fn do_type_char(char_typed: char, before: &str) -> Option<(String, SingleFileChange)> {
        let (offset, before) = extract_offset(before);
        let edit = TextEdit::insert(offset, char_typed.to_string());
        let before = edit.apply(&before);
        let parse = SourceFile::parse(&before);
        on_char_typed_inner(&parse.tree(), offset, char_typed)
            .map(|it| (it.edit.apply(&before), it))
    }

    fn type_char(char_typed: char, before: &str, after: &str) {
        let (actual, file_change) = do_type_char(char_typed, before)
            .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));

        if after.contains("<|>") {
            let (offset, after) = extract_offset(after);
            assert_eq_text!(&after, &actual);
            assert_eq!(file_change.cursor_position, Some(offset))
        } else {
            assert_eq_text!(after, &actual);
        }
    }

    fn type_char_noop(char_typed: char, before: &str) {
        let file_change = do_type_char(char_typed, before);
        assert!(file_change.is_none())
    }

    #[test]
    fn test_on_eq_typed() {
        //     do_check(r"
        // fn foo() {
        //     let foo =<|>
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        // }
        // ");
        type_char(
            '=',
            r"
fn foo() {
    let foo <|> 1 + 1
}
",
            r"
fn foo() {
    let foo = 1 + 1;
}
",
        );
        //     do_check(r"
        // fn foo() {
        //     let foo =<|>
        //     let bar = 1;
        // }
        // ", r"
        // fn foo() {
        //     let foo =;
        //     let bar = 1;
        // }
        // ");
    }

    #[test]
    fn indents_new_chain_call() {
        type_char(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    <|>
            }
            ",
        )
    }

    #[test]
    fn indents_new_chain_call_with_semi() {
        type_char(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                <|>;
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .;
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    <|>;
            }
            ",
        )
    }

    #[test]
    fn indents_continued_chain_call() {
        type_char(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                <|>
            }
            ",
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    .
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                self.child_impl(db, name)
                    .first()
                    <|>
            }
            ",
        );
    }

    #[test]
    fn indents_middle_of_chain_call() {
        type_char(
            '.',
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                <|>
                    .nth(92)
                    .unwrap();
            }
            ",
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                    .
                    .nth(92)
                    .unwrap();
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            fn source_impl() {
                let var = enum_defvariant_list().unwrap()
                    <|>
                    .nth(92)
                    .unwrap();
            }
            ",
        );
    }

    #[test]
    fn dont_indent_freestanding_dot() {
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
                <|>
            }
            ",
        );
        type_char_noop(
            '.',
            r"
            pub fn child(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
            <|>
            }
            ",
        );
    }

    #[test]
    fn adds_space_after_return_type() {
        type_char('>', "fn foo() -<|>{ 92 }", "fn foo() -><|> { 92 }")
    }
}
<|endoftext|>


<commit_before>//! Syntax Tree library used throughout the rust analyzer.
//!
//! Properties:
//!   - easy and fast incremental re-parsing
//!   - graceful handling of errors
//!   - full-fidelity representation (*any* text can be precisely represented as
//!     a syntax tree)
//!
//! For more information, see the [RFC]. Current implementation is inspired by
//! the [Swift] one.
//!
//! The most interesting modules here are `syntax_node` (which defines concrete
//! syntax tree) and `ast` (which defines abstract syntax tree on top of the
//! CST). The actual parser live in a separate `ra_parser` crate, thought the
//! lexer lives in this crate.
//!
//! See `api_walkthrough` test in this file for a quick API tour!
//!
//! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256>
//! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md>

mod syntax_node;
mod syntax_text;
mod syntax_error;
mod parsing;
mod validation;
mod ptr;

pub mod algo;
pub mod ast;
#[doc(hidden)]
pub mod fuzz;

pub use rowan::{SmolStr, TextRange, TextUnit};
pub use ra_parser::SyntaxKind;
pub use ra_parser::T;
pub use crate::{
    ast::AstNode,
    syntax_error::{SyntaxError, SyntaxErrorKind, Location},
    syntax_text::SyntaxText,
    syntax_node::{Direction,  SyntaxNode, WalkEvent, TreeArc, SyntaxTreeBuilder, SyntaxElement, SyntaxToken, InsertPosition},
    ptr::{SyntaxNodePtr, AstPtr},
    parsing::{tokenize, classify_literal, Token},
};

use ra_text_edit::AtomTextEdit;
use crate::syntax_node::GreenNode;

/// `SourceFile` represents a parse tree for a single Rust file.
pub use crate::ast::SourceFile;

impl SourceFile {
    fn new(green: GreenNode, errors: Vec<SyntaxError>) -> TreeArc<SourceFile> {
        let root = SyntaxNode::new(green, errors);
        if cfg!(debug_assertions) {
            validation::validate_block_structure(&root);
        }
        assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
        TreeArc::cast(root)
    }

    pub fn parse(text: &str) -> TreeArc<SourceFile> {
        let (green, errors) = parsing::parse_text(text);
        SourceFile::new(green, errors)
    }

    pub fn reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> {
        self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
    }

    pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<TreeArc<SourceFile>> {
        parsing::incremental_reparse(self.syntax(), edit, self.errors())
            .map(|(green_node, errors, _reparsed_range)| SourceFile::new(green_node, errors))
    }

    fn full_reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> {
        let text = edit.apply(self.syntax().text().to_string());
        SourceFile::parse(&text)
    }

    pub fn errors(&self) -> Vec<SyntaxError> {
        let mut errors = self.syntax.root_data().to_vec();
        errors.extend(validation::validate(self));
        errors
    }
}

/// This test does not assert anything and instead just shows off the crate's
/// API.
#[test]
fn api_walkthrough() {
    use ast::{ModuleItemOwner, NameOwner};

    let source_code = "
        fn foo() {
            1 + 1
        }
    ";
    // `SourceFile` is the main entry point.
    //
    // Note how `parse` does not return a `Result`: even completely invalid
    // source code might be parsed.
    let file = SourceFile::parse(source_code);

    // Due to the way ownership is set up, owned syntax Nodes always live behind
    // a `TreeArc` smart pointer. `TreeArc` is roughly an `std::sync::Arc` which
    // points to the whole file instead of an individual node.
    let file: TreeArc<SourceFile> = file;

    // `SourceFile` is the root of the syntax tree. We can iterate file's items:
    let mut func = None;
    for item in file.items() {
        match item.kind() {
            ast::ModuleItemKind::FnDef(f) => func = Some(f),
            _ => unreachable!(),
        }
    }
    // The returned items are always references.
    let func: &ast::FnDef = func.unwrap();

    // All nodes implement `ToOwned` trait, with `Owned = TreeArc<Self>`.
    // `to_owned` is a cheap operation: atomic increment.
    let _owned_func: TreeArc<ast::FnDef> = func.to_owned();

    // Each AST node has a bunch of getters for children. All getters return
    // `Option`s though, to account for incomplete code. Some getters are common
    // for several kinds of node. In this case, a trait like `ast::NameOwner`
    // usually exists. By convention, all ast types should be used with `ast::`
    // qualifier.
    let name: Option<&ast::Name> = func.name();
    let name = name.unwrap();
    assert_eq!(name.text(), "foo");

    // Let's get the `1 + 1` expression!
    let block: &ast::Block = func.body().unwrap();
    let expr: &ast::Expr = block.expr().unwrap();

    // "Enum"-like nodes are represented using the "kind" pattern. It allows us
    // to match exhaustively against all flavors of nodes, while maintaining
    // internal representation flexibility. The drawback is that one can't write
    // nested matches as one pattern.
    let bin_expr: &ast::BinExpr = match expr.kind() {
        ast::ExprKind::BinExpr(e) => e,
        _ => unreachable!(),
    };

    // Besides the "typed" AST API, there's an untyped CST one as well.
    // To switch from AST to CST, call `.syntax()` method:
    let expr_syntax: &SyntaxNode = expr.syntax();

    // Note how `expr` and `bin_expr` are in fact the same node underneath:
    assert!(std::ptr::eq(expr_syntax, bin_expr.syntax()));

    // To go from CST to AST, `AstNode::cast` function is used:
    let expr = match ast::Expr::cast(expr_syntax) {
        Some(e) => e,
        None => unreachable!(),
    };

    // Note how expr is also a reference!
    let expr: &ast::Expr = expr;

    // This is possible because the underlying representation is the same:
    assert_eq!(
        expr as *const ast::Expr as *const u8,
        expr_syntax as *const SyntaxNode as *const u8
    );

    // The two properties each syntax node has is a `SyntaxKind`:
    assert_eq!(expr_syntax.kind(), SyntaxKind::BIN_EXPR);

    // And text range:
    assert_eq!(expr_syntax.range(), TextRange::from_to(32.into(), 37.into()));

    // You can get node's text as a `SyntaxText` object, which will traverse the
    // tree collecting token's text:
    let text: SyntaxText<'_> = expr_syntax.text();
    assert_eq!(text.to_string(), "1 + 1");

    // There's a bunch of traversal methods on `SyntaxNode`:
    assert_eq!(expr_syntax.parent(), Some(block.syntax()));
    assert_eq!(block.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{']));
    assert_eq!(
        expr_syntax.next_sibling_or_token().map(|it| it.kind()),
        Some(SyntaxKind::WHITESPACE)
    );

    // As well as some iterator helpers:
    let f = expr_syntax.ancestors().find_map(ast::FnDef::cast);
    assert_eq!(f, Some(&*func));
    assert!(expr_syntax.siblings_with_tokens(Direction::Next).any(|it| it.kind() == T!['}']));
    assert_eq!(
        expr_syntax.descendants_with_tokens().count(),
        8, // 5 tokens `1`, ` `, `+`, ` `, `!`
           // 2 child literal expressions: `1`, `1`
           // 1 the node itself: `1 + 1`
    );

    // There's also a `preorder` method with a more fine-grained iteration control:
    let mut buf = String::new();
    let mut indent = 0;
    for event in expr_syntax.preorder_with_tokens() {
        match event {
            WalkEvent::Enter(node) => {
                let text = match node {
                    SyntaxElement::Node(it) => it.text().to_string(),
                    SyntaxElement::Token(it) => it.text().to_string(),
                };
                buf += &format!("{:indent$}{:?} {:?}\n", " ", text, node.kind(), indent = indent);
                indent += 2;
            }
            WalkEvent::Leave(_) => indent -= 2,
        }
    }
    assert_eq!(indent, 0);
    assert_eq!(
        buf.trim(),
        r#"
"1 + 1" BIN_EXPR
  "1" LITERAL
    "1" INT_NUMBER
  " " WHITESPACE
  "+" PLUS
  " " WHITESPACE
  "1" LITERAL
    "1" INT_NUMBER
"#
        .trim()
    );

    // To recursively process the tree, there are three approaches:
    // 1. explicitly call getter methods on AST nodes.
    // 2. use descendants and `AstNode::cast`.
    // 3. use descendants and the visitor.
    //
    // Here's how the first one looks like:
    let exprs_cast: Vec<String> = file
        .syntax()
        .descendants()
        .filter_map(ast::Expr::cast)
        .map(|expr| expr.syntax().text().to_string())
        .collect();

    // An alternative is to use a visitor. The visitor does not do traversal
    // automatically (so it's more akin to a generic lambda) and is constructed
    // from closures. This seems more flexible than a single generated visitor
    // trait.
    use algo::visit::{visitor, Visitor};
    let mut exprs_visit = Vec::new();
    for node in file.syntax().descendants() {
        if let Some(result) =
            visitor().visit::<ast::Expr, _>(|expr| expr.syntax().text().to_string()).accept(node)
        {
            exprs_visit.push(result);
        }
    }
    assert_eq!(exprs_cast, exprs_visit);
}
<commit_msg>add Parse<commit_after>//! Syntax Tree library used throughout the rust analyzer.
//!
//! Properties:
//!   - easy and fast incremental re-parsing
//!   - graceful handling of errors
//!   - full-fidelity representation (*any* text can be precisely represented as
//!     a syntax tree)
//!
//! For more information, see the [RFC]. Current implementation is inspired by
//! the [Swift] one.
//!
//! The most interesting modules here are `syntax_node` (which defines concrete
//! syntax tree) and `ast` (which defines abstract syntax tree on top of the
//! CST). The actual parser live in a separate `ra_parser` crate, thought the
//! lexer lives in this crate.
//!
//! See `api_walkthrough` test in this file for a quick API tour!
//!
//! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256>
//! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md>

mod syntax_node;
mod syntax_text;
mod syntax_error;
mod parsing;
mod validation;
mod ptr;

pub mod algo;
pub mod ast;
#[doc(hidden)]
pub mod fuzz;

use std::sync::Arc;

use ra_text_edit::AtomTextEdit;

use crate::syntax_node::GreenNode;

pub use rowan::{SmolStr, TextRange, TextUnit};
pub use ra_parser::SyntaxKind;
pub use ra_parser::T;
pub use crate::{
    ast::AstNode,
    syntax_error::{SyntaxError, SyntaxErrorKind, Location},
    syntax_text::SyntaxText,
    syntax_node::{Direction,  SyntaxNode, WalkEvent, TreeArc, SyntaxTreeBuilder, SyntaxElement, SyntaxToken, InsertPosition},
    ptr::{SyntaxNodePtr, AstPtr},
    parsing::{tokenize, classify_literal, Token},
};

/// `Parse` is the result of the parsing: a syntax tree and a collection of
/// errors.
///
/// Note that we always produce a syntax tree, even for completely invalid
/// files.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Parse {
    pub tree: TreeArc<SourceFile>,
    pub errors: Arc<Vec<SyntaxError>>,
}

impl Parse {
    pub fn ok(self) -> Result<TreeArc<SourceFile>, Arc<Vec<SyntaxError>>> {
        if self.errors.is_empty() {
            Ok(self.tree)
        } else {
            Err(self.errors)
        }
    }
}

/// `SourceFile` represents a parse tree for a single Rust file.
pub use crate::ast::SourceFile;

impl SourceFile {
    fn new(green: GreenNode, errors: Vec<SyntaxError>) -> TreeArc<SourceFile> {
        let root = SyntaxNode::new(green, errors);
        if cfg!(debug_assertions) {
            validation::validate_block_structure(&root);
        }
        assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
        TreeArc::cast(root)
    }

    pub fn parse(text: &str) -> TreeArc<SourceFile> {
        let (green, errors) = parsing::parse_text(text);
        SourceFile::new(green, errors)
    }

    pub fn reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> {
        self.incremental_reparse(edit).unwrap_or_else(|| self.full_reparse(edit))
    }

    pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<TreeArc<SourceFile>> {
        parsing::incremental_reparse(self.syntax(), edit, self.errors())
            .map(|(green_node, errors, _reparsed_range)| SourceFile::new(green_node, errors))
    }

    fn full_reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> {
        let text = edit.apply(self.syntax().text().to_string());
        SourceFile::parse(&text)
    }

    pub fn errors(&self) -> Vec<SyntaxError> {
        let mut errors = self.syntax.root_data().to_vec();
        errors.extend(validation::validate(self));
        errors
    }
}

/// This test does not assert anything and instead just shows off the crate's
/// API.
#[test]
fn api_walkthrough() {
    use ast::{ModuleItemOwner, NameOwner};

    let source_code = "
        fn foo() {
            1 + 1
        }
    ";
    // `SourceFile` is the main entry point.
    //
    // Note how `parse` does not return a `Result`: even completely invalid
    // source code might be parsed.
    let file = SourceFile::parse(source_code);

    // Due to the way ownership is set up, owned syntax Nodes always live behind
    // a `TreeArc` smart pointer. `TreeArc` is roughly an `std::sync::Arc` which
    // points to the whole file instead of an individual node.
    let file: TreeArc<SourceFile> = file;

    // `SourceFile` is the root of the syntax tree. We can iterate file's items:
    let mut func = None;
    for item in file.items() {
        match item.kind() {
            ast::ModuleItemKind::FnDef(f) => func = Some(f),
            _ => unreachable!(),
        }
    }
    // The returned items are always references.
    let func: &ast::FnDef = func.unwrap();

    // All nodes implement `ToOwned` trait, with `Owned = TreeArc<Self>`.
    // `to_owned` is a cheap operation: atomic increment.
    let _owned_func: TreeArc<ast::FnDef> = func.to_owned();

    // Each AST node has a bunch of getters for children. All getters return
    // `Option`s though, to account for incomplete code. Some getters are common
    // for several kinds of node. In this case, a trait like `ast::NameOwner`
    // usually exists. By convention, all ast types should be used with `ast::`
    // qualifier.
    let name: Option<&ast::Name> = func.name();
    let name = name.unwrap();
    assert_eq!(name.text(), "foo");

    // Let's get the `1 + 1` expression!
    let block: &ast::Block = func.body().unwrap();
    let expr: &ast::Expr = block.expr().unwrap();

    // "Enum"-like nodes are represented using the "kind" pattern. It allows us
    // to match exhaustively against all flavors of nodes, while maintaining
    // internal representation flexibility. The drawback is that one can't write
    // nested matches as one pattern.
    let bin_expr: &ast::BinExpr = match expr.kind() {
        ast::ExprKind::BinExpr(e) => e,
        _ => unreachable!(),
    };

    // Besides the "typed" AST API, there's an untyped CST one as well.
    // To switch from AST to CST, call `.syntax()` method:
    let expr_syntax: &SyntaxNode = expr.syntax();

    // Note how `expr` and `bin_expr` are in fact the same node underneath:
    assert!(std::ptr::eq(expr_syntax, bin_expr.syntax()));

    // To go from CST to AST, `AstNode::cast` function is used:
    let expr = match ast::Expr::cast(expr_syntax) {
        Some(e) => e,
        None => unreachable!(),
    };

    // Note how expr is also a reference!
    let expr: &ast::Expr = expr;

    // This is possible because the underlying representation is the same:
    assert_eq!(
        expr as *const ast::Expr as *const u8,
        expr_syntax as *const SyntaxNode as *const u8
    );

    // The two properties each syntax node has is a `SyntaxKind`:
    assert_eq!(expr_syntax.kind(), SyntaxKind::BIN_EXPR);

    // And text range:
    assert_eq!(expr_syntax.range(), TextRange::from_to(32.into(), 37.into()));

    // You can get node's text as a `SyntaxText` object, which will traverse the
    // tree collecting token's text:
    let text: SyntaxText<'_> = expr_syntax.text();
    assert_eq!(text.to_string(), "1 + 1");

    // There's a bunch of traversal methods on `SyntaxNode`:
    assert_eq!(expr_syntax.parent(), Some(block.syntax()));
    assert_eq!(block.syntax().first_child_or_token().map(|it| it.kind()), Some(T!['{']));
    assert_eq!(
        expr_syntax.next_sibling_or_token().map(|it| it.kind()),
        Some(SyntaxKind::WHITESPACE)
    );

    // As well as some iterator helpers:
    let f = expr_syntax.ancestors().find_map(ast::FnDef::cast);
    assert_eq!(f, Some(&*func));
    assert!(expr_syntax.siblings_with_tokens(Direction::Next).any(|it| it.kind() == T!['}']));
    assert_eq!(
        expr_syntax.descendants_with_tokens().count(),
        8, // 5 tokens `1`, ` `, `+`, ` `, `!`
           // 2 child literal expressions: `1`, `1`
           // 1 the node itself: `1 + 1`
    );

    // There's also a `preorder` method with a more fine-grained iteration control:
    let mut buf = String::new();
    let mut indent = 0;
    for event in expr_syntax.preorder_with_tokens() {
        match event {
            WalkEvent::Enter(node) => {
                let text = match node {
                    SyntaxElement::Node(it) => it.text().to_string(),
                    SyntaxElement::Token(it) => it.text().to_string(),
                };
                buf += &format!("{:indent$}{:?} {:?}\n", " ", text, node.kind(), indent = indent);
                indent += 2;
            }
            WalkEvent::Leave(_) => indent -= 2,
        }
    }
    assert_eq!(indent, 0);
    assert_eq!(
        buf.trim(),
        r#"
"1 + 1" BIN_EXPR
  "1" LITERAL
    "1" INT_NUMBER
  " " WHITESPACE
  "+" PLUS
  " " WHITESPACE
  "1" LITERAL
    "1" INT_NUMBER
"#
        .trim()
    );

    // To recursively process the tree, there are three approaches:
    // 1. explicitly call getter methods on AST nodes.
    // 2. use descendants and `AstNode::cast`.
    // 3. use descendants and the visitor.
    //
    // Here's how the first one looks like:
    let exprs_cast: Vec<String> = file
        .syntax()
        .descendants()
        .filter_map(ast::Expr::cast)
        .map(|expr| expr.syntax().text().to_string())
        .collect();

    // An alternative is to use a visitor. The visitor does not do traversal
    // automatically (so it's more akin to a generic lambda) and is constructed
    // from closures. This seems more flexible than a single generated visitor
    // trait.
    use algo::visit::{visitor, Visitor};
    let mut exprs_visit = Vec::new();
    for node in file.syntax().descendants() {
        if let Some(result) =
            visitor().visit::<ast::Expr, _>(|expr| expr.syntax().text().to_string()).accept(node)
        {
            exprs_visit.push(result);
        }
    }
    assert_eq!(exprs_cast, exprs_visit);
}
<|endoftext|>