feat: Fortran module (#6685)

---------

Co-authored-by: David Knaack <davidkna@users.noreply.github.com>
This commit is contained in:
Adrian Mysak
2025-10-21 20:17:08 +02:00
committed by GitHub
parent 3b5bbe3566
commit 90d8c4a256
15 changed files with 537 additions and 0 deletions
+139
View File
@@ -530,6 +530,54 @@
"disabled": false
}
},
"fortran": {
"$ref": "#/$defs/FortranConfig",
"default": {
"format": "via [$symbol($version(-$name) )]($style)",
"version_format": "${raw}",
"symbol": "🅵 ",
"style": "bold purple",
"disabled": false,
"detect_extensions": [
"f",
"F",
"for",
"FOR",
"ftn",
"FTN",
"f77",
"F77",
"f90",
"F90",
"f95",
"F95",
"f03",
"F03",
"f08",
"F08",
"f18",
"F18"
],
"detect_files": [
"fpm.toml"
],
"detect_folders": [],
"commands": [
[
"gfortran",
"--version"
],
[
"flang",
"--version"
],
[
"flang-new",
"--version"
]
]
}
},
"fossil_branch": {
"$ref": "#/$defs/FossilBranchConfig",
"default": {
@@ -3083,6 +3131,97 @@
},
"additionalProperties": false
},
"FortranConfig": {
"type": "object",
"properties": {
"format": {
"type": "string",
"default": "via [$symbol($version(-$name) )]($style)"
},
"version_format": {
"type": "string",
"default": "${raw}"
},
"symbol": {
"type": "string",
"default": "🅵 "
},
"style": {
"type": "string",
"default": "bold purple"
},
"disabled": {
"type": "boolean",
"default": false
},
"detect_extensions": {
"type": "array",
"items": {
"type": "string"
},
"default": [
"f",
"F",
"for",
"FOR",
"ftn",
"FTN",
"f77",
"F77",
"f90",
"F90",
"f95",
"F95",
"f03",
"F03",
"f08",
"F08",
"f18",
"F18"
]
},
"detect_files": {
"type": "array",
"items": {
"type": "string"
},
"default": [
"fpm.toml"
]
},
"detect_folders": {
"type": "array",
"items": {
"type": "string"
},
"default": []
},
"commands": {
"type": "array",
"items": {
"type": "array",
"items": {
"type": "string"
}
},
"default": [
[
"gfortran",
"--version"
],
[
"flang",
"--version"
],
[
"flang-new",
"--version"
]
]
}
},
"additionalProperties": false
},
"FossilBranchConfig": {
"type": "object",
"properties": {
+38
View File
@@ -293,6 +293,7 @@ $elixir\
$elm\
$erlang\
$fennel\
$fortran\
$gleam\
$golang\
$guix_shell\
@@ -1673,6 +1674,43 @@ Produces a prompt that looks like:
AA -------------------------------------------- BB -------------------------------------------- CC
```
## Fortran
The `fortran` module shows the current compiler version of Fortran.
### Options
| Option | Default | Description |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `symbol` | `' '` | The symbol used before displaying the version of COBOL. |
| `format` | `'via [$symbol($version )]($style)'` | The format for the module. |
| `version_format` | `'${raw}'` | The version format. Available vars are `raw`, `major`, `minor`, & `patch` |
| `style` | `'bold purple'` | The style for the module. |
| `detect_extensions` | `['f', 'F', 'for', 'FOR', 'ftn', 'FTN', 'f77', 'F77', 'f90', 'F90', 'f95', 'F95','f03', 'F03', 'f08', 'F08', 'f18', 'F18']` | Which extensions should trigger this module. |
| `detect_files` | `['fpm.toml']` | Which filenames should trigger this module. |
| `detect_folders` | `[]` | Which folders should trigger this module. |
| `commands` | `[ [ 'gfortran', '--version' ], [ 'flang', '--version' ], [ 'flang-new', '--version' ] ]` | How to detect what the compiler is |
| `disabled` | `false` | Disables the `fortran` module. |
### Variables
| Variable | Example | Description |
| -------- | -------- | ------------------------------------ |
| name | gfortran | The name of the compiler |
| version | `14.2.0` | The version of the Fortran compiler |
| symbol | | Mirrors the value of option `symbol` |
| style\* | | Mirrors the value of option `style` |
*: This variable can only be used as a part of a style string
### Commands
The `commands` option accepts a list of commands to determine the compiler version and name.
Each command is represented as a list of the executable name, followed by its arguments, usually something like `['myfortran', '--version']`. Starship will try executing each command until it gets a result on STDOUT.
If a Fortran compiler is not supported by this module, you can request it by [raising an issue on GitHub](https://github.com/starship/starship/).
## Fossil Branch
The `fossil_branch` module shows the name of the active branch of the check-out in your current directory.
@@ -67,6 +67,9 @@ format = '\[[$symbol($version)]($style)\]'
[fennel]
format = '\[[$symbol($version)]($style)\]'
[fortran]
format = '\[[$symbol($version)]($style)\]'
[fossil_branch]
format = '\[[$symbol$branch]($style)\]'
+1
View File
@@ -40,6 +40,7 @@ $elixir\
$elm\
$erlang\
$fennel\
$fortran\
$golang\
$guix_shell\
$haskell\
@@ -43,6 +43,9 @@ symbol = " "
[fennel]
symbol = " "
[fortran]
symbol = " "
[fossil_branch]
symbol = " "
@@ -43,6 +43,9 @@ format = '(via [$symbol($version )]($style))'
[fennel]
format = '(via [$symbol($version )]($style))'
[fortran]
format = "(via [$symbol($version )]($style))"
[gleam]
format = '(via [$symbol($version )]($style))'
@@ -8,6 +8,9 @@ empty_symbol = "❗ "
[erlang]
symbol = "ⓔ "
[fortran]
symbol = "F "
[nodejs]
symbol = "[⬢](bold green) "
@@ -43,6 +43,9 @@ format = 'via [$symbol]($style)'
[fennel]
format = 'via [$symbol]($style)'
[fortran]
format = 'via [$symbol]($style)'
[gleam]
format = 'via [$symbol]($style)'
@@ -89,6 +89,9 @@ symbol = "erl "
[fennel]
symbol = "fnl "
[fortran]
symbol = "fortran "
[fossil_branch]
symbol = "fossil "
truncation_symbol = "..."
+44
View File
@@ -0,0 +1,44 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Deserialize, Serialize)]
#[cfg_attr(
feature = "config-schema",
derive(schemars::JsonSchema),
schemars(deny_unknown_fields)
)]
#[serde(default)]
pub struct FortranConfig<'a> {
pub format: &'a str,
pub version_format: &'a str,
pub symbol: &'a str,
pub style: &'a str,
pub disabled: bool,
pub detect_extensions: Vec<&'a str>,
pub detect_files: Vec<&'a str>,
pub detect_folders: Vec<&'a str>,
pub commands: Vec<Vec<&'a str>>,
}
impl Default for FortranConfig<'_> {
fn default() -> Self {
FortranConfig {
format: "via [$symbol($version(-$name) )]($style)",
version_format: "${raw}",
symbol: "🅵 ",
style: "bold purple",
disabled: false,
detect_extensions: vec![
"f", "F", "for", "FOR", "ftn", "FTN", "f77", "F77", "f90", "F90", "f95", "F95",
"f03", "F03", "f08", "F08", "f18", "F18",
],
detect_files: vec!["fpm.toml"],
detect_folders: vec![],
commands: vec![
// first check for most common 'gfortran' by default
vec!["gfortran", "--version"],
vec!["flang", "--version"],
vec!["flang-new", "--version"],
],
}
}
}
+3
View File
@@ -30,6 +30,7 @@ pub mod env_var;
pub mod erlang;
pub mod fennel;
pub mod fill;
pub mod fortran;
pub mod fossil_branch;
pub mod fossil_metrics;
pub mod gcloud;
@@ -176,6 +177,8 @@ pub struct FullConfig<'a> {
#[serde(borrow)]
fill: fill::FillConfig<'a>,
#[serde(borrow)]
fortran: fortran::FortranConfig<'a>,
#[serde(borrow)]
fossil_branch: fossil_branch::FossilBranchConfig<'a>,
#[serde(borrow)]
fossil_metrics: fossil_metrics::FossilMetricsConfig<'a>,
+1
View File
@@ -67,6 +67,7 @@ pub const PROMPT_ORDER: &[&str] = &[
"elm",
"erlang",
"fennel",
"fortran",
"gleam",
"golang",
"gradle",
+1
View File
@@ -34,6 +34,7 @@ pub const ALL_MODULES: &[&str] = &[
"erlang",
"fennel",
"fill",
"fortran",
"fossil_branch",
"fossil_metrics",
"gcloud",
+289
View File
@@ -0,0 +1,289 @@
use std::{borrow::Cow, ops::Deref, sync::LazyLock};
use semver::Version;
use crate::{
config::ModuleConfig,
configs::fortran::FortranConfig,
formatter::{StringFormatter, VersionFormatter},
};
use super::{Context, Module};
pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
let mut module = context.new_module("fortran");
let config = FortranConfig::try_load(module.config);
let is_fortran_project = context
.try_begin_scan()?
.set_files(&config.detect_files)
.set_extensions(&config.detect_extensions)
.set_folders(&config.detect_folders)
.is_match();
if !is_fortran_project {
return None;
}
let parsed = StringFormatter::new(config.format).and_then(|formatter| {
let compiler_info = LazyLock::new(|| context.exec_cmds_return_first(&config.commands));
formatter
.map_meta(|var, _| match var {
"symbol" => Some(config.symbol),
_ => None,
})
.map_style(|variable| match variable {
"style" => Some(Ok(config.style)),
_ => None,
})
.map(|variable| match variable {
"name" => {
let compiler_info = &compiler_info.deref().as_ref()?.stdout;
let compiler = if compiler_info.contains("GNU") {
"gfortran"
} else if compiler_info.contains("flang-new") || compiler_info.contains("flang")
{
"flang"
} else {
return None;
};
Some(Ok(Cow::Borrowed(compiler)))
}
"version" => {
let compiler_info = &compiler_info.deref().as_ref()?.stdout;
VersionFormatter::format_module_version(
module.get_name(),
compiler_info
.split_whitespace()
.find(|word| Version::parse(word).is_ok())?,
config.version_format,
)
.map(Cow::Owned)
.map(Ok)
}
_ => None,
})
.parse(None, Some(context))
});
module.set_segments(match parsed {
Ok(segments) => segments,
Err(error) => {
log::warn!("Error in module `fortran`:\n{}", error);
return None;
}
});
Some(module)
}
#[cfg(test)]
mod tests {
use std::{fs::File, io};
use nu_ansi_term::Color;
use crate::{test::ModuleRenderer, utils::CommandOutput};
#[test]
fn folder_without_fortran_file() -> io::Result<()> {
let dir = tempfile::tempdir()?;
let actual = ModuleRenderer::new("fortran").path(dir.path()).collect();
let expected = None;
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_f_fortran_file() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("test.f"))?.sync_all()?;
let actual = ModuleRenderer::new("fortran")
.cmd(
"gfortran --version",
Some(CommandOutput {
stdout: String::from(
"\
GNU Fortran (Homebrew GCC 14.2.0_1) 14.2.0
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n",
),
stderr: String::default(),
}),
)
.path(dir.path())
.collect();
let expected = Some(format!(
"via {}",
Color::Purple.bold().paint("🅵 14.2.0-gfortran ")
));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_f_fortran_file_but_no_compiler() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("test.f"))?.sync_all()?;
let actual = ModuleRenderer::new("fortran")
.cmd("gfortran --version", None)
.cmd("flang --version", None)
.cmd("flang-new --version", None)
.path(dir.path())
.collect();
let expected = Some(format!("via {}", Color::Purple.bold().paint("🅵 ")));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_f_fortran_file_and_flang() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("test.f"))?.sync_all()?;
let actual = ModuleRenderer::new("fortran")
.cmd("gfortran --version", None)
.cmd(
"flang --version",
Some(CommandOutput {
stdout: String::from(
"\
Homebrew flang version 20.1.3
Target: arm64-apple-darwin24.3.0
Thread model: posix
InstalledDir: /opt/homebrew/Cellar/flang/20.1.3/libexec
Configuration file: /opt/homebrew/Cellar/flang/20.1.3/libexec/flang.cfg
Configuration file: /opt/homebrew/etc/clang/arm64-apple-darwin24.cfg",
),
stderr: String::default(),
}),
)
.path(dir.path())
.collect();
let expected = Some(format!(
"via {}",
Color::Purple.bold().paint("🅵 20.1.3-flang ")
));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_f_fortran_file_without_gfortran_flang_falls_to_flang_new() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("test.f"))?.sync_all()?;
let actual = ModuleRenderer::new("fortran")
.cmd("gfortran --version", None)
.cmd("flang --version", None)
.cmd(
"flang-new --version",
Some(CommandOutput {
stdout: String::from(
"\
Homebrew flang version 20.1.3
Target: arm64-apple-darwin24.3.0
Thread model: posix
InstalledDir: /opt/homebrew/Cellar/flang/20.1.3/libexec
Configuration file: /opt/homebrew/Cellar/flang/20.1.3/libexec/flang.cfg
Configuration file: /opt/homebrew/etc/clang/arm64-apple-darwin24.cfg",
),
stderr: String::default(),
}),
)
.path(dir.path())
.collect();
let expected = Some(format!(
"via {}",
Color::Purple.bold().paint("🅵 20.1.3-flang ")
));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_capital_f18_fortran_file() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("test.F18"))?.sync_all()?;
let actual = ModuleRenderer::new("fortran")
.cmd(
"gfortran --version",
Some(CommandOutput {
stdout: String::from(
"\
GNU Fortran (Homebrew GCC 14.2.0_1) 14.2.0
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n",
),
stderr: String::default(),
}),
)
.path(dir.path())
.collect();
let expected = Some(format!(
"via {}",
Color::Purple.bold().paint("🅵 14.2.0-gfortran ")
));
assert_eq!(expected, actual);
dir.close()
}
#[test]
fn folder_with_fpm_config_file() -> io::Result<()> {
let dir = tempfile::tempdir()?;
File::create(dir.path().join("fpm.toml"))?.sync_all()?;
let actual = ModuleRenderer::new("fortran")
.cmd(
"gfortran --version",
Some(CommandOutput {
stdout: String::from(
"\
GNU Fortran (Homebrew GCC 14.2.0_1) 14.2.0
Copyright (C) 2024 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n",
),
stderr: String::default(),
}),
)
.path(dir.path())
.collect();
let expected = Some(format!(
"via {}",
Color::Purple.bold().paint("🅵 14.2.0-gfortran ")
));
assert_eq!(expected, actual);
dir.close()
}
}
+3
View File
@@ -27,6 +27,7 @@ mod env_var;
mod erlang;
mod fennel;
mod fill;
mod fortran;
mod fossil_branch;
mod fossil_metrics;
mod gcloud;
@@ -145,6 +146,7 @@ pub fn handle<'a>(module: &str, context: &'a Context) -> Option<Module<'a>> {
"env_var" => env_var::module(None, context),
"fennel" => fennel::module(context),
"fill" => fill::module(context),
"fortran" => fortran::module(context),
"fossil_branch" => fossil_branch::module(context),
"fossil_metrics" => fossil_metrics::module(context),
"gcloud" => gcloud::module(context),
@@ -276,6 +278,7 @@ pub fn description(module: &str) -> &'static str {
"erlang" => "Current OTP version",
"fennel" => "The currently installed version of Fennel",
"fill" => "Fills the remaining space on the line with a pad string",
"fortran" => "The currently used version of Fortran",
"fossil_branch" => "The active branch of the check-out in your current directory",
"fossil_metrics" => "The currently added/deleted lines in your check-out",
"gcloud" => "The current GCP client configuration",