feat(ocaml): Add OCaml module (#1218)

* Add OCaml module

* Update README.md accordingly

* Fix typo

* Add Reason support

Obtain OCaml version using `-vnum` flag.
Extend conditions to match projects using jbuild, esy
or merlin.

* Update README.md

Co-authored-by: Dario Vladovic <d.vladimyr@gmail.com>
This commit is contained in:
Hirochika Matsumoto 2020-05-22 01:43:13 +09:00 committed by GitHub
parent 1b2ea77b1c
commit 9e8f35d746
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 250 additions and 0 deletions

View file

@ -116,6 +116,7 @@ prompt_order = [
"java",
"julia",
"nodejs",
"ocaml",
"php",
"python",
"ruby",
@ -1098,6 +1099,35 @@ package, and shows its current version. The module currently supports `npm`, `ca
symbol = "🎁 "
```
## OCaml
The `ocaml` module shows the currently installed version of OCaml.
The module will be shown if any of the following conditions are met:
- The current directory contains a file with `.opam` extension or `_opam` directory
- The current directory contains a `esy.lock` directory
- The current directory contains a `dune` or `dune-project` file
- The current directory contains a `jbuild` or `jbuild-ignore` file
- The current directory contains a `.merlin` file
- The current directory contains a file with `.ml`, `.mli`, `.re` or `.rei` extension
### Options
| Variable | Default | Description |
| ---------- | --------------- | ------------------------------------------------------- |
| `symbol` | `"🐫 "` | The symbol used before displaying the version of OCaml. |
| `style` | `"bold yellow"` | The style for the module. |
| `disabled` | `false` | Disables the `ocaml` module. |
### Example
```toml
# ~/.config/starship.toml
[ocaml]
symbol = "🐪 "
```
## PHP
The `php` module shows the currently installed version of PHP.

View file

@ -27,6 +27,7 @@ pub mod kubernetes;
pub mod memory_usage;
pub mod nix_shell;
pub mod nodejs;
pub mod ocaml;
pub mod package;
pub mod php;
pub mod python;

23
src/configs/ocaml.rs Normal file
View file

@ -0,0 +1,23 @@
use crate::config::{ModuleConfig, RootModuleConfig, SegmentConfig};
use ansi_term::{Color, Style};
use starship_module_config_derive::ModuleConfig;
#[derive(Clone, ModuleConfig)]
pub struct OCamlConfig<'a> {
pub symbol: SegmentConfig<'a>,
pub version: SegmentConfig<'a>,
pub style: Style,
pub disabled: bool,
}
impl<'a> RootModuleConfig<'a> for OCamlConfig<'a> {
fn new() -> Self {
OCamlConfig {
symbol: SegmentConfig::new("🐫 "),
version: SegmentConfig::default(),
style: Color::Yellow.bold(),
disabled: false,
}
}
}

View file

@ -40,6 +40,7 @@ impl<'a> RootModuleConfig<'a> for StarshipRootConfig<'a> {
"java",
"julia",
"nodejs",
"ocaml",
"php",
"python",
"ruby",

View file

@ -39,6 +39,7 @@ pub const ALL_MODULES: &[&str] = &[
"memory_usage",
"nix_shell",
"nodejs",
"ocaml",
"package",
"python",
"ruby",

View file

@ -28,6 +28,7 @@ mod line_break;
mod memory_usage;
mod nix_shell;
mod nodejs;
mod ocaml;
mod package;
mod php;
mod python;
@ -79,6 +80,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
"memory_usage" => memory_usage::module(context),
"nix_shell" => nix_shell::module(context),
"nodejs" => nodejs::module(context),
"ocaml" => ocaml::module(context),
"package" => package::module(context),
"php" => php::module(context),
"python" => python::module(context),
@ -127,6 +129,7 @@ pub fn description(module: &str) -> &'static str {
"memory_usage" => "Current system memory and swap usage",
"nix_shell" => "The nix-shell environment",
"nodejs" => "The currently installed version of NodeJS",
"ocaml" => "The currently installed version of OCaml",
"package" => "The package version of the current directory's project",
"php" => "The currently installed version of PHP",
"python" => "The currently installed version of Python",

187
src/modules/ocaml.rs Normal file
View file

@ -0,0 +1,187 @@
use super::{Context, Module, RootModuleConfig, SegmentConfig};
use crate::configs::ocaml::OCamlConfig;
use crate::utils;
/// Creates a module with the current OCaml version
///
/// Will display the OCaml version if any of the following criteria are met:
/// - Current directory contains a file with `.opam` extension or `_opam` directory
/// - Current directory contains a `esy.lock` directory
/// - Current directory contains a `dune` or `dune-project` file
/// - Current directory contains a `jbuild` or `jbuild-ignore` file
/// - Current directory contains a `.merlin` file
/// - Current directory contains a file with `.ml`, `.mli`, `.re` or `.rei` extension
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let is_ocaml_project = context
.try_begin_scan()?
.set_files(&["dune", "dune-project", "jbuild", "jbuild-ignore", ".merlin"])
.set_folders(&["_opam", "esy.lock"])
.set_extensions(&["opam", "ml", "mli", "re", "rei"])
.is_match();
if !is_ocaml_project {
return None;
}
let ocaml_version = utils::exec_cmd("ocaml", &["-vnum"])?.stdout;
let formatted_version = format!("v{}", &ocaml_version);
let mut module = context.new_module("ocaml");
let config = OCamlConfig::try_load(module.config);
module.set_style(config.style);
module.create_segment("symbol", &config.symbol);
module.create_segment("version", &SegmentConfig::new(&formatted_version));
Some(module)
}
#[cfg(test)]
mod tests {
use crate::modules::utils::test::render_module;
use ansi_term::Color;
use std::fs::{self, File};
use std::io;
#[test]
fn folder_without_ocaml_file() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let actual = render_module("ocaml", dir.path(), None);
let expected = None;
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_opam_file() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.opam"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None);
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_opam_directory() -> io::Result<()> {
let dir = tempfile::tempdir()?;
fs::create_dir_all(dir.path().join("_opam"))?;
let actual = render_module("ocaml", dir.path(), None);
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_esy_lock_directory() -> io::Result<()> {
let dir = tempfile::tempdir()?;
fs::create_dir_all(dir.path().join("esy.lock"))?;
let actual = render_module("ocaml", dir.path(), None);
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_dune() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("dune"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None);
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_dune_project() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("dune-project"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None);
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_jbuild() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("jbuild"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None);
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_jbuild_ignore() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("jbuild-ignore"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None);
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_merlin_file() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join(".merlin"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None);
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_ml_file() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.ml"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None);
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_mli_file() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.mli"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None);
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_re_file() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.re"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None);
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_rei_file() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("any.rei"))?.sync_all()?;
let actual = render_module("ocaml", dir.path(), None);
let expected = Some(format!("via {} ", Color::Yellow.bold().paint("🐫 v4.10.0")));
assert_eq!(expected, actual);
dir.close()
}
}

View file

@ -63,6 +63,10 @@ pub fn exec_cmd(cmd: &str, args: &[&str]) -> Option<CommandOutput> {
stdout: String::from("v12.0.0"),
stderr: String::default(),
}),
"ocaml -vnum" => Some(CommandOutput {
stdout: String::from("4.10.0"),
stderr: String::default(),
}),
"php -r echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION.'.'.PHP_RELEASE_VERSION;" => {
Some(CommandOutput {
stdout: String::from("7.3.8"),