diff --git a/src/lib/builtins/echo.rs b/src/lib/builtins/echo.rs
index ac1148e335395a72633931b1f593e0ff859c12c9..ebd5b702714cb3c1bfb3cd45e093c9d0bab40d90 100644
--- a/src/lib/builtins/echo.rs
+++ b/src/lib/builtins/echo.rs
@@ -1,8 +1,41 @@
-use crate::types;
+use super::Status;
+use crate as ion_shell;
+use crate::{types, Shell};
+use builtins_proc::builtin;
 use smallvec::SmallVec;
 use std::io::{self, BufWriter, Write};
 
-pub fn echo(args: &[types::Str]) -> Result<(), io::Error> {
+#[builtin(
+    desc = "display text",
+    man = "
+SYNOPSIS
+    echo [ -h | --help ] [-e] [-n] [-s] [STRING]...
+
+DESCRIPTION
+    Print the STRING(s) to standard output.
+
+OPTIONS
+    -e
+        enable the interpretation of backslash escapes
+    -n
+        do not output the trailing newline
+    -s
+        do not separate arguments with spaces
+
+    Escape Sequences
+        When the -e argument is used, the following sequences will be interpreted:
+        \\\\  backslash
+        \\a  alert (BEL)
+        \\b  backspace (BS)
+        \\c  produce no further output
+        \\e  escape (ESC)
+        \\f  form feed (FF)
+        \\n  new line
+        \\r  carriage return
+        \\t  horizontal tab (HT)
+        \\v  vertical tab (VT)"
+)]
+pub fn echo(args: &[types::Str], _: &mut Shell<'_>) -> Status {
     let mut escape = false;
     let mut newline = true;
     let mut spaces = true;
@@ -49,78 +82,76 @@ pub fn echo(args: &[types::Str]) -> Result<(), io::Error> {
     let stdout = io::stdout();
     let mut buffer = BufWriter::new(stdout.lock());
 
-    let mut first = true;
-    for arg in data[1..].iter().map(|x| x.as_bytes()) {
-        if first {
+    let mut inner = || -> std::io::Result<()> {
+        let mut first = true;
+        for arg in data[1..].iter().map(|x| x.as_bytes()) {
+            if spaces && !first {
+                buffer.write_all(&[b' '])?;
+            }
             first = false;
-        } else if spaces {
-            buffer.write_all(&[b' '])?;
-        }
 
-        if escape {
-            let mut check = false;
-            for &byte in arg {
-                match byte {
-                    b'\\' if check => {
-                        buffer.write_all(&[byte])?;
-                        check = false;
-                    }
-                    b'\\' => check = true,
-                    b'a' if check => {
-                        buffer.write_all(&[7u8])?; // bell
-                        check = false;
-                    }
-                    b'b' if check => {
-                        buffer.write_all(&[8u8])?; // backspace
-                        check = false;
-                    }
-                    b'c' if check => {
-                        buffer.flush()?;
-                        return Ok(());
-                    }
-                    b'e' if check => {
-                        buffer.write_all(&[27u8])?; // escape
-                        check = false;
-                    }
-                    b'f' if check => {
-                        buffer.write_all(&[12u8])?; // form feed
-                        check = false;
-                    }
-                    b'n' if check => {
-                        buffer.write_all(&[b'\n'])?; // newline
-                        check = false;
-                    }
-                    b'r' if check => {
-                        buffer.write_all(&[b'\r'])?;
-                        check = false;
-                    }
-                    b't' if check => {
-                        buffer.write_all(&[b'\t'])?;
-                        check = false;
-                    }
-                    b'v' if check => {
-                        buffer.write_all(&[11u8])?; // vertical tab
-                        check = false;
-                    }
-                    _ if check => {
-                        buffer.write_all(&[b'\\', byte])?;
-                        check = false;
-                    }
-                    _ => {
-                        buffer.write_all(&[byte])?;
+            if escape {
+                let mut check = false;
+                for &byte in arg {
+                    match byte {
+                        b'\\' if check => {
+                            buffer.write_all(&[byte])?;
+                            check = false;
+                        }
+                        b'\\' => check = true,
+                        b'a' if check => {
+                            buffer.write_all(&[7u8])?; // bell
+                            check = false;
+                        }
+                        b'b' if check => {
+                            buffer.write_all(&[8u8])?; // backspace
+                            check = false;
+                        }
+                        b'c' if check => {
+                            return Ok(());
+                        }
+                        b'e' if check => {
+                            buffer.write_all(&[27u8])?; // escape
+                            check = false;
+                        }
+                        b'f' if check => {
+                            buffer.write_all(&[12u8])?; // form feed
+                            check = false;
+                        }
+                        b'n' if check => {
+                            buffer.write_all(&[b'\n'])?; // newline
+                            check = false;
+                        }
+                        b'r' if check => {
+                            buffer.write_all(&[b'\r'])?;
+                            check = false;
+                        }
+                        b't' if check => {
+                            buffer.write_all(&[b'\t'])?;
+                            check = false;
+                        }
+                        b'v' if check => {
+                            buffer.write_all(&[11u8])?; // vertical tab
+                            check = false;
+                        }
+                        _ if check => {
+                            buffer.write_all(&[b'\\', byte])?;
+                            check = false;
+                        }
+                        _ => {
+                            buffer.write_all(&[byte])?;
+                        }
                     }
                 }
+            } else {
+                buffer.write_all(arg)?;
             }
-        } else {
-            buffer.write_all(arg)?;
         }
-    }
-
-    if newline {
-        buffer.write_all(&[b'\n'])?;
-    }
-
-    buffer.flush()?;
+        if newline {
+            buffer.write_all(&[b'\n'])?;
+        }
+        Ok(())
+    };
 
-    Ok(())
+    inner().and_then(|_| buffer.flush()).into()
 }
diff --git a/src/lib/builtins/helpers.rs b/src/lib/builtins/helpers.rs
index 3d948a6f0800984b0fb1261ee2db80f580087caa..22cdd38c35cec3d00115e328f7072c9931d18965 100644
--- a/src/lib/builtins/helpers.rs
+++ b/src/lib/builtins/helpers.rs
@@ -47,3 +47,12 @@ impl<'a> From<Status> for Value<types::Function<'a>> {
 impl From<Status> for types::Str {
     fn from(status: Status) -> Self { types::Str::from(status.as_os_code().to_string()) }
 }
+
+impl From<std::io::Result<()>> for Status {
+    fn from(res: std::io::Result<()>) -> Self {
+        match res {
+            Ok(_) => Status::SUCCESS,
+            Err(err) => Status::error(format!("{}", err)),
+        }
+    }
+}
diff --git a/src/lib/builtins/is.rs b/src/lib/builtins/is.rs
index 350f016bf83e616719fe8c5b93b28909469813b4..0abd3b10d984a326d597d903364bf0d5705122d9 100644
--- a/src/lib/builtins/is.rs
+++ b/src/lib/builtins/is.rs
@@ -1,6 +1,22 @@
 use super::Status;
+use crate as ion_shell;
 use crate::{shell::Shell, types};
+use builtins_proc::builtin;
 
+// TODO: Add support for multiple name in builtins man
+#[builtin(
+    desc = "checks if two arguments are the same",
+    man = "
+SYNOPSIS
+    is [ -h | --help ] [not]
+
+DESCRIPTION
+    Returns 0 if the two arguments are equal
+
+OPTIONS
+    not
+        returns 0 if the two arguments are not equal."
+)]
 pub fn is(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
     match args.len() {
         4 => {
@@ -52,21 +68,21 @@ fn test_is() {
     shell.variables_mut().set("y", "0");
 
     // Four arguments
-    assert!(is(&vec_string(&["is", " ", " ", " "]), &mut shell).is_failure());
-    assert!(is(&vec_string(&["is", "not", " ", " "]), &mut shell).is_failure());
-    assert!(is(&vec_string(&["is", "not", "$x", "$x"]), &mut shell).is_failure());
-    assert!(is(&vec_string(&["is", "not", "2", "1"]), &mut shell).is_success());
-    assert!(is(&vec_string(&["is", "not", "$x", "$y"]), &mut shell).is_success());
+    assert!(builtin_is(&vec_string(&["is", " ", " ", " "]), &mut shell).is_failure());
+    assert!(builtin_is(&vec_string(&["is", "not", " ", " "]), &mut shell).is_failure());
+    assert!(builtin_is(&vec_string(&["is", "not", "$x", "$x"]), &mut shell).is_failure());
+    assert!(builtin_is(&vec_string(&["is", "not", "2", "1"]), &mut shell).is_success());
+    assert!(builtin_is(&vec_string(&["is", "not", "$x", "$y"]), &mut shell).is_success());
 
     // Three arguments
-    assert!(is(&vec_string(&["is", "1", "2"]), &mut shell).is_failure());
-    assert!(is(&vec_string(&["is", "$x", "$y"]), &mut shell).is_failure());
-    assert!(is(&vec_string(&["is", " ", " "]), &mut shell).is_success());
-    assert!(is(&vec_string(&["is", "$x", "$x"]), &mut shell).is_success());
+    assert!(builtin_is(&vec_string(&["is", "1", "2"]), &mut shell).is_failure());
+    assert!(builtin_is(&vec_string(&["is", "$x", "$y"]), &mut shell).is_failure());
+    assert!(builtin_is(&vec_string(&["is", " ", " "]), &mut shell).is_success());
+    assert!(builtin_is(&vec_string(&["is", "$x", "$x"]), &mut shell).is_success());
 
     // Two arguments
-    assert!(is(&vec_string(&["is", " "]), &mut shell).is_failure());
+    assert!(builtin_is(&vec_string(&["is", " "]), &mut shell).is_failure());
 
     // One argument
-    assert!(is(&vec_string(&["is"]), &mut shell).is_failure());
+    assert!(builtin_is(&vec_string(&["is"]), &mut shell).is_failure());
 }
diff --git a/src/lib/builtins/man_pages.rs b/src/lib/builtins/man_pages.rs
index 20be9eeb6f06a8abc27302f52a18d418829ff941..cc38ebf1dd0e664f1ce1e389e3a400a17cec9633 100644
--- a/src/lib/builtins/man_pages.rs
+++ b/src/lib/builtins/man_pages.rs
@@ -60,115 +60,6 @@ DESCRIPTION
 // example 1
 //"#;
 
-pub const MAN_READ: &str = r#"NAME
-    read - read a line of input into some variables
-
-SYNOPSIS
-    read VARIABLES...
-
-DESCRIPTION
-    For each variable reads from standard input and stores the results in the variable."#;
-
-pub const MAN_DROP: &str = r#"NAME
-    drop - delete some variables or arrays
-
-SYNOPSIS
-    drop [ -a ] VARIABLES...
-
-DESCRIPTION
-    Deletes the variables given to it as arguments. The variables name must be supplied.
-    Instead of '$x' use 'x'.
-
-OPTIONS
-    -a
-        Instead of deleting variables deletes arrays."#;
-
-pub const MAN_SET: &str = r#"NAME
-    set - Set or unset values of shell options and positional parameters.
-
-SYNOPSIS
-    set [ --help ] [-e | +e] [-x | +x] [-o [vi | emacs]] [- | --] [STRING]...
-
-DESCRIPTION
-    Shell options may be set using the '-' character, and unset using the '+' character.
-
-OPTIONS
-    -e  Exit immediately if a command exits with a non-zero status.
-
-    -o  Specifies that an argument will follow that sets the key map.
-        The keymap argument may be either `vi` or `emacs`.
-
-    -x  Specifies that commands will be printed as they are executed.
-
-    --  Following arguments will be set as positional arguments in the shell.
-        If no argument are supplied, arguments will be unset.
-
-    -   Following arguments will be set as positional arguments in the shell.
-        If no arguments are suppled, arguments will not be unset."#;
-
-pub const MAN_EQ: &str = r#"NAME
-    eq - Checks if two arguments are the same
-
-SYNOPSIS
-    eq [ -h | --help ] [not]
-
-DESCRIPTION
-    Returns 0 if the two arguments are equal
-
-OPTIONS
-    not
-        returns 0 if the two arguments are not equal."#;
-
-pub const MAN_EVAL: &str = r#"NAME
-    eval - evaluates the specified commands
-
-SYNOPSIS
-    eval COMMANDS...
-
-DESCRIPTION
-    eval evaluates the given arguments as a command. If more than one argument is given,
-    all arguments are joined using a space as a separator."#;
-
-pub const MAN_SOURCE: &str = r#"NAME
-    source - evaluates given file
-
-SYNOPSIS
-    source FILEPATH
-
-DESCRIPTION
-    Evaluates the commands in a specified file in the current shell. All changes in shell
-    variables will affect the current shell because of this."#;
-
-pub const MAN_ECHO: &str = r#"NAME
-    echo - display a line of text
-
-SYNOPSIS
-    echo [ -h | --help ] [-e] [-n] [-s] [STRING]...
-
-DESCRIPTION
-    Print the STRING(s) to standard output.
-
-OPTIONS
-    -e
-        enable the interpretation of backslash escapes
-    -n
-        do not output the trailing newline
-    -s
-        do not separate arguments with spaces
-
-    Escape Sequences
-        When the -e argument is used, the following sequences will be interpreted:
-        \\  backslash
-        \a  alert (BEL)
-        \b  backspace (BS)
-        \c  produce no further output
-        \e  escape (ESC)
-        \f  form feed (FF)
-        \n  new line
-        \r  carriage return
-        \t  horizontal tab (HT)
-        \v  vertical tab (VT)"#;
-
 pub const MAN_RANDOM: &str = r#"NAME
     random - generate a random number
 
@@ -181,42 +72,6 @@ DESCRIPTION
     The range depends on what arguments you pass. If no arguments are given the range is [0, 32767].
     If two arguments are given the range is [START, END]."#;
 
-pub const MAN_TRUE: &str = r#"NAME
-    true - does nothing successfully
-
-SYNOPSIS
-    true
-
-DESCRIPTION
-    Sets the exit status to 0."#;
-
-pub const MAN_FALSE: &str = r#"NAME
-    false - does nothing unsuccessfully
-
-SYNOPSIS
-    false
-
-DESCRIPTION
-    Sets the exit status to 1."#;
-
-pub const MAN_JOBS: &str = r#"NAME
-    jobs - list all jobs running in the background
-
-SYNOPSIS
-    jobs
-
-DESCRIPTION
-    Prints a list of all jobs running in the background."#;
-
-pub const MAN_BG: &str = r#"NAME
-    bg - sends jobs to background
-
-SYNOPSIS
-    bg PID
-
-DESCRIPTION
-    bg sends the job to the background resuming it if it has stopped."#;
-
 pub const MAN_FG: &str = r#"NAME
     fg - bring job to foreground
 
diff --git a/src/lib/builtins/mod.rs b/src/lib/builtins/mod.rs
index f94703d54a8a5622d4808bf15811af5430729e9f..e1b9712ea49a0a24e586ecc3d1522abaa5061ca2 100644
--- a/src/lib/builtins/mod.rs
+++ b/src/lib/builtins/mod.rs
@@ -19,12 +19,13 @@ mod variables;
 use self::{
     command_info::{find_type, which},
     conditionals::{contains, ends_with, starts_with},
-    echo::echo,
+    echo::builtin_echo,
     exists::exists,
     functions::print_functions,
-    is::is,
+    is::builtin_is,
     man_pages::*,
-    source::source,
+    set::builtin_set,
+    source::builtin_source,
     status::builtin_status,
     test::test,
     variables::{alias, drop_alias, drop_array, drop_variable},
@@ -199,10 +200,10 @@ impl<'a> BuiltinMap<'a> {
     pub fn with_values_tests(&mut self) -> &mut Self {
         self.add("bool", &builtin_bool, "If the value is '1' or 'true', return 0 exit status")
             .add("calc", &builtin_calc, "Calculate a mathematical expression")
-            .add("eq", &builtin_eq, "Simple alternative to == and !=")
-            .add("is", &builtin_eq, "Simple alternative to == and !=")
-            .add("true", &builtin_true, "Do nothing, successfully")
-            .add("false", &builtin_false, "Do nothing, unsuccessfully")
+            .add("eq", &builtin_is, "Simple alternative to == and !=")
+            .add("is", &builtin_is, "Simple alternative to == and !=")
+            .add("true", &builtin_tru, "Do nothing, successfully")
+            .add("false", &builtin_fals, "Do nothing, unsuccessfully")
             .add(
                 "starts-with",
                 &starts_with,
@@ -575,11 +576,17 @@ impl Completer for EmptyCompleter {
     fn completions(&mut self, _start: &str) -> Vec<String> { Vec::new() }
 }
 
-pub fn builtin_read(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
-    if check_help(args, MAN_READ) {
-        return Status::SUCCESS;
-    }
+#[builtin(
+    desc = "read a line of input into some variables",
+    man = "
+SYNOPSIS
+    read VARIABLES...
 
+DESCRIPTION
+    For each variable reads from standard input and stores the results in the variable.
+"
+)]
+pub fn read(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
     if atty::is(atty::Stream::Stdin) {
         let mut con = Context::new();
         for arg in args.iter().skip(1) {
@@ -603,10 +610,22 @@ pub fn builtin_read(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
     Status::SUCCESS
 }
 
-pub fn builtin_drop(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
-    if check_help(args, MAN_DROP) {
-        return Status::SUCCESS;
-    }
+#[builtin(
+    desc = "delete some variables or arrays",
+    man = "
+SYNOPSIS
+    drop [ -a ] VARIABLES...
+
+DESCRIPTION
+    Deletes the variables given to it as arguments. The variables name must be supplied.
+    Instead of '$x' use 'x'.
+
+OPTIONS
+    -a
+        Instead of deleting variables deletes arrays.
+"
+)]
+pub fn drop(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
     if args.len() >= 2 && args[1] == "-a" {
         drop_array(shell.variables_mut(), args)
     } else {
@@ -614,46 +633,20 @@ pub fn builtin_drop(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
     }
 }
 
-pub fn builtin_set(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
-    if check_help(args, MAN_SET) {
-        return Status::SUCCESS;
-    }
-    set::set(args, shell)
-}
-
-pub fn builtin_eq(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
-    if check_help(args, MAN_EQ) {
-        return Status::SUCCESS;
-    }
-
-    is(args, shell)
-}
-
-pub fn builtin_eval(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
-    if check_help(args, MAN_EVAL) {
-        Status::SUCCESS
-    } else {
-        shell.execute_command(args[1..].join(" ").as_bytes()).unwrap_or_else(|_| {
-            Status::error("ion: supplied eval expression was not terminated".to_string())
-        })
-    }
-}
-
-pub fn builtin_source(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
-    if check_help(args, MAN_SOURCE) {
-        return Status::SUCCESS;
-    }
-    source(shell, args)
-}
+#[builtin(
+    desc = "evaluates the specified commands",
+    man = "
+SYNOPSIS
+    eval COMMANDS...
 
-pub fn builtin_echo(args: &[types::Str], _: &mut Shell<'_>) -> Status {
-    if check_help(args, MAN_ECHO) {
-        return Status::SUCCESS;
-    }
-    match echo(args) {
-        Ok(()) => Status::SUCCESS,
-        Err(why) => Status::error(why.to_string()),
-    }
+DESCRIPTION
+    eval evaluates the given arguments as a command. If more than one argument is given,
+    all arguments are joined using a space as a separator."
+)]
+pub fn eval(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
+    shell.execute_command(args[1..].join(" ").as_bytes()).unwrap_or_else(|_| {
+        Status::error("ion: supplied eval expression was not terminated".to_string())
+    })
 }
 
 pub fn builtin_test(args: &[types::Str], _: &mut Shell<'_>) -> Status {
@@ -684,15 +677,30 @@ pub fn builtin_random(args: &[types::Str], _: &mut Shell<'_>) -> Status {
     }
 }
 
-pub fn builtin_true(args: &[types::Str], _: &mut Shell<'_>) -> Status {
-    check_help(args, MAN_TRUE);
+// TODO: Find a workaround
+#[builtin(
+    desc = "does nothing sucessfully",
+    man = "
+SYNOPSIS
+    true
+
+DESCRIPTION
+    Sets the exit status to 0."
+)]
+pub fn tru(args: &[types::Str], _: &mut Shell<'_>) -> Status {
     Status::SUCCESS
 }
 
-pub fn builtin_false(args: &[types::Str], _: &mut Shell<'_>) -> Status {
-    if check_help(args, MAN_FALSE) {
-        return Status::SUCCESS;
-    }
+#[builtin(
+    desc = "does nothing unsuccessfully",
+    man = "
+SYNOPSIS
+    false
+
+DESCRIPTION
+    Sets the exit status to 1."
+)]
+pub fn fals(args: &[types::Str], _: &mut Shell<'_>) -> Status {
     Status::error("")
 }
 
@@ -702,16 +710,30 @@ pub fn builtin_wait(_: &[types::Str], shell: &mut Shell<'_>) -> Status {
     Status::SUCCESS
 }
 
-pub fn builtin_jobs(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
-    check_help(args, MAN_JOBS);
+#[builtin(
+    desc = "list all jobs running in the background",
+    man = "
+SYNOPSIS
+    jobs
+
+DESCRIPTION
+    Prints a list of all jobs running in the background."
+)]
+pub fn jobs(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
     job_control::jobs(shell);
     Status::SUCCESS
 }
 
-pub fn builtin_bg(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
-    if check_help(args, MAN_BG) {
-        return Status::SUCCESS;
-    }
+#[builtin(
+    desc = "sends jobs to background",
+    man = "
+SYNOPSIS
+    bg PID
+
+DESCRIPTION
+    bg sends the job to the background resuming it if it has stopped."
+)]
+pub fn bg(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
     job_control::bg(shell, &args[1..])
 }
 
diff --git a/src/lib/builtins/set.rs b/src/lib/builtins/set.rs
index d6ee04e04697be813d8ec5486735a5cfa2e576d9..1294d0af83a4b79e1b3475acb3e68a95e1298c52 100644
--- a/src/lib/builtins/set.rs
+++ b/src/lib/builtins/set.rs
@@ -1,8 +1,10 @@
 use super::Status;
+use crate as ion_shell;
 use crate::{
     shell::{variables::Value, Shell},
     types,
 };
+use builtins_proc::builtin;
 use std::iter;
 
 enum PositionalArgs {
@@ -12,6 +14,29 @@ enum PositionalArgs {
 
 use self::PositionalArgs::*;
 
+#[builtin(
+    desc = "Set or unset values of shell options and positional parameters.",
+    man = "
+SYNOPSIS
+    set [ --help ] [-e | +e] [-x | +x] [-o [vi | emacs]] [- | --] [STRING]...
+
+DESCRIPTION
+    Shell options may be set using the '-' character, and unset using the '+' character.
+
+OPTIONS
+    -e  Exit immediately if a command exits with a non-zero status.
+
+    -o  Specifies that an argument will follow that sets the key map.
+        The keymap argument may be either `vi` or `emacs`.
+
+    -x  Specifies that commands will be printed as they are executed.
+
+    --  Following arguments will be set as positional arguments in the shell.
+        If no argument are supplied, arguments will be unset.
+
+    -   Following arguments will be set as positional arguments in the shell.
+        If no arguments are suppled, arguments will not be unset."
+)]
 pub fn set(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
     let mut args_iter = args.iter();
     let mut positionals = None;
diff --git a/src/lib/builtins/source.rs b/src/lib/builtins/source.rs
index e9d1eca2110f6cc6eb2b661920444abbc4803c60..aa1dca552d015f447eeb1de41a96a69c6987eef0 100644
--- a/src/lib/builtins/source.rs
+++ b/src/lib/builtins/source.rs
@@ -1,10 +1,21 @@
 use super::Status;
+use crate as ion_shell;
 use crate::{shell::Shell, types};
+use builtins_proc::builtin;
 use std::fs::File;
 
-/// Evaluates the given file and returns 'SUCCESS' if it succeeds.
-pub fn source(shell: &mut Shell<'_>, arguments: &[types::Str]) -> Status {
-    match arguments.get(1) {
+#[builtin(
+    desc = "evaluates given file",
+    man = "
+SYNOPSIS
+    source FILEPATH
+
+DESCRIPTION
+    Evaluates the commands in a specified file in the current shell. All changes in shell
+    variables will affect the current shell because of this."
+)]
+pub fn source(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
+    match args.get(1) {
         Some(argument) => {
             if let Ok(file) = File::open(argument.as_str()) {
                 if let Err(why) = shell.execute_command(file) {