diff --git a/docs/config/README.md b/docs/config/README.md index b7381ee74..44b8aadc9 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -460,9 +460,19 @@ The `dotnet` module shows the relevant version of the .NET Core SDK for the curr the SDK has been pinned in the current directory, the pinned version is shown. Otherwise the module shows the latest installed version of the SDK. -This module will only be shown in your prompt when one of the following files are present in the -current directory: `global.json`, `project.json`, `*.sln`, `*.csproj`, `*.fsproj`, `*.xproj`. You'll -also need the .NET Core command-line tools installed in order to use it correctly. +This module will only be shown in your prompt when one or more of the following files are present in the +current directory: +* `global.json` +* `project.json` +* `Directory.Build.props` +* `Directory.Build.targets` +* `Packages.props` +* `*.sln` +* `*.csproj` +* `*.fsproj` +* `*.xproj` + +You'll also need the .NET Core SDK installed in order to use it correctly. Internally, this module uses its own mechanism for version detection. Typically it is twice as fast as running `dotnet --version`, but it may show an incorrect version if your .NET project has an diff --git a/src/modules/dotnet.rs b/src/modules/dotnet.rs index 2c0169ba0..763032e22 100644 --- a/src/modules/dotnet.rs +++ b/src/modules/dotnet.rs @@ -12,6 +12,9 @@ type JValue = serde_json::Value; const GLOBAL_JSON_FILE: &str = "global.json"; const PROJECT_JSON_FILE: &str = "project.json"; +const DIRECTORY_BUILD_PROPS_FILE: &str = "Directory.Build.props"; +const DIRECTORY_BUILD_TARGETS_FILE: &str = "Directory.Build.targets"; +const PACKAGES_PROPS_FILE: &str = "Packages.props"; /// A module which shows the latest (or pinned) version of the dotnet SDK /// @@ -23,7 +26,13 @@ pub fn module<'a>(context: &'a Context) -> Option> { // check for the version using the JSON files let is_dotnet_project = context .try_begin_scan()? - .set_files(&[GLOBAL_JSON_FILE, PROJECT_JSON_FILE]) + .set_files(&[ + GLOBAL_JSON_FILE, + PROJECT_JSON_FILE, + DIRECTORY_BUILD_PROPS_FILE, + DIRECTORY_BUILD_TARGETS_FILE, + PACKAGES_PROPS_FILE, + ]) .set_extensions(&["sln", "csproj", "fsproj", "xproj"]) .is_match(); @@ -196,6 +205,7 @@ fn get_dotnet_file_type(path: &Path) -> Option { match extension_lower.as_ref().map(|f| f.as_ref()) { Some("sln") => return Some(FileType::SolutionFile), Some("csproj") | Some("fsproj") | Some("xproj") => return Some(FileType::ProjectFile), + Some("props") | Some("targets") => return Some(FileType::MsBuildFile), _ => (), }; @@ -259,6 +269,7 @@ enum FileType { ProjectFile, GlobalJson, SolutionFile, + MsBuildFile, } struct Version(String); diff --git a/tests/testsuite/dotnet.rs b/tests/testsuite/dotnet.rs index fe6991ade..7af9c3247 100644 --- a/tests/testsuite/dotnet.rs +++ b/tests/testsuite/dotnet.rs @@ -1,9 +1,14 @@ use super::common; +use regex::Regex; use std::fs::{DirBuilder, OpenOptions}; use std::io::{self, Error, ErrorKind, Write}; use std::process::{Command, Stdio}; use tempfile::{self, TempDir}; +const DOTNET_OUTPUT_PATTERN: &str = "•NET v\\d+?\\.\\d+?\\.\\d?"; +const DOTNET_PINNED_VERSION: &str = "1.2.3"; +const DOTNET_PINNED_VERSION_OUTPUT_PATTERN: &str = "•NET v1\\.2\\.3"; + #[test] #[ignore] fn shows_nothing_in_directory_with_zero_relevant_files() -> io::Result<()> { @@ -12,12 +17,39 @@ fn shows_nothing_in_directory_with_zero_relevant_files() -> io::Result<()> { workspace.close() } +#[test] +#[ignore] +fn shows_latest_in_directory_with_directory_build_props_file() -> io::Result<()> { + let workspace = create_workspace(false)?; + touch_path(&workspace, "Directory.Build.props", None)?; + expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?; + workspace.close() +} + +#[test] +#[ignore] +fn shows_latest_in_directory_with_directory_build_targets_file() -> io::Result<()> { + let workspace = create_workspace(false)?; + touch_path(&workspace, "Directory.Build.targets", None)?; + expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?; + workspace.close() +} + +#[test] +#[ignore] +fn shows_latest_in_directory_with_packages_props_file() -> io::Result<()> { + let workspace = create_workspace(false)?; + touch_path(&workspace, "Packages.props", None)?; + expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?; + workspace.close() +} + #[test] #[ignore] fn shows_latest_in_directory_with_solution() -> io::Result<()> { let workspace = create_workspace(false)?; touch_path(&workspace, "solution.sln", None)?; - expect_output(&workspace, ".", Some("•NET v2.2.402"))?; + expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?; workspace.close() } @@ -26,7 +58,7 @@ fn shows_latest_in_directory_with_solution() -> io::Result<()> { fn shows_latest_in_directory_with_csproj() -> io::Result<()> { let workspace = create_workspace(false)?; touch_path(&workspace, "project.csproj", None)?; - expect_output(&workspace, ".", Some("•NET v2.2.402"))?; + expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?; workspace.close() } @@ -35,7 +67,7 @@ fn shows_latest_in_directory_with_csproj() -> io::Result<()> { fn shows_latest_in_directory_with_fsproj() -> io::Result<()> { let workspace = create_workspace(false)?; touch_path(&workspace, "project.fsproj", None)?; - expect_output(&workspace, ".", Some("•NET v2.2.402"))?; + expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?; workspace.close() } @@ -44,7 +76,7 @@ fn shows_latest_in_directory_with_fsproj() -> io::Result<()> { fn shows_latest_in_directory_with_xproj() -> io::Result<()> { let workspace = create_workspace(false)?; touch_path(&workspace, "project.xproj", None)?; - expect_output(&workspace, ".", Some("•NET v2.2.402"))?; + expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?; workspace.close() } @@ -53,7 +85,7 @@ fn shows_latest_in_directory_with_xproj() -> io::Result<()> { fn shows_latest_in_directory_with_project_json() -> io::Result<()> { let workspace = create_workspace(false)?; touch_path(&workspace, "project.json", None)?; - expect_output(&workspace, ".", Some("•NET v2.2.402"))?; + expect_output(&workspace, ".", Some(DOTNET_OUTPUT_PATTERN))?; workspace.close() } @@ -61,9 +93,9 @@ fn shows_latest_in_directory_with_project_json() -> io::Result<()> { #[ignore] fn shows_pinned_in_directory_with_global_json() -> io::Result<()> { let workspace = create_workspace(false)?; - let global_json = make_pinned_sdk_json("1.2.3"); + let global_json = make_pinned_sdk_json(DOTNET_PINNED_VERSION); touch_path(&workspace, "global.json", Some(&global_json))?; - expect_output(&workspace, ".", Some("•NET v1.2.3"))?; + expect_output(&workspace, ".", Some(DOTNET_PINNED_VERSION_OUTPUT_PATTERN))?; workspace.close() } @@ -71,10 +103,14 @@ fn shows_pinned_in_directory_with_global_json() -> io::Result<()> { #[ignore] fn shows_pinned_in_project_below_root_with_global_json() -> io::Result<()> { let workspace = create_workspace(false)?; - let global_json = make_pinned_sdk_json("1.2.3"); + let global_json = make_pinned_sdk_json(DOTNET_PINNED_VERSION); touch_path(&workspace, "global.json", Some(&global_json))?; touch_path(&workspace, "project/project.csproj", None)?; - expect_output(&workspace, "project", Some("•NET v1.2.3"))?; + expect_output( + &workspace, + "project", + Some(DOTNET_PINNED_VERSION_OUTPUT_PATTERN), + )?; workspace.close() } @@ -82,10 +118,14 @@ fn shows_pinned_in_project_below_root_with_global_json() -> io::Result<()> { #[ignore] fn shows_pinned_in_deeply_nested_project_within_repository() -> io::Result<()> { let workspace = create_workspace(true)?; - let global_json = make_pinned_sdk_json("1.2.3"); + let global_json = make_pinned_sdk_json(DOTNET_PINNED_VERSION); touch_path(&workspace, "global.json", Some(&global_json))?; touch_path(&workspace, "deep/path/to/project/project.csproj", None)?; - expect_output(&workspace, "deep/path/to/project", Some("•NET v1.2.3"))?; + expect_output( + &workspace, + "deep/path/to/project", + Some(DOTNET_PINNED_VERSION_OUTPUT_PATTERN), + )?; workspace.close() } @@ -137,7 +177,7 @@ fn make_pinned_sdk_json(version: &str) -> String { json_text.replace("INSERT_VERSION", version) } -fn expect_output(workspace: &TempDir, run_from: &str, contains: Option<&str>) -> io::Result<()> { +fn expect_output(workspace: &TempDir, run_from: &str, pattern: Option<&str>) -> io::Result<()> { let run_path = workspace.path().join(run_from); let output = common::render_module("dotnet") .current_dir(run_path) @@ -147,8 +187,11 @@ fn expect_output(workspace: &TempDir, run_from: &str, contains: Option<&str>) -> // This can be helpful for debugging eprintln!("The dotnet module showed: {}", text); - match contains { - Some(contains) => assert!(text.contains(contains)), + match pattern { + Some(pattern) => { + let re = Regex::new(pattern).unwrap(); + assert!(re.is_match(&text)); + } None => assert!(text.is_empty()), }