toolchain.rs

  1//! Provides core data types for language toolchains.
  2//!
  3//! A language can have associated toolchains,
  4//! which is a set of tools used to interact with the projects written in said language.
  5//! For example, a Python project can have an associated virtual environment; a Rust project can have a toolchain override.
  6
  7use std::{path::Path, sync::Arc};
  8
  9use gpui::SharedString;
 10use util::rel_path::RelPath;
 11
 12use crate::{LanguageName, ManifestName};
 13
 14/// Represents a single toolchain.
 15#[derive(Clone, Eq, Debug)]
 16pub struct Toolchain {
 17    /// User-facing label
 18    pub name: SharedString,
 19    /// Absolute path
 20    pub path: SharedString,
 21    pub language_name: LanguageName,
 22    /// Full toolchain data (including language-specific details)
 23    pub as_json: serde_json::Value,
 24}
 25
 26impl std::hash::Hash for Toolchain {
 27    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 28        let Self {
 29            name,
 30            path,
 31            language_name,
 32            as_json: _,
 33        } = self;
 34        name.hash(state);
 35        path.hash(state);
 36        language_name.hash(state);
 37    }
 38}
 39
 40impl PartialEq for Toolchain {
 41    fn eq(&self, other: &Self) -> bool {
 42        let Self {
 43            name,
 44            path,
 45            language_name,
 46            as_json: _,
 47        } = self;
 48        // Do not use as_json for comparisons; it shouldn't impact equality, as it's not user-surfaced.
 49        // Thus, there could be multiple entries that look the same in the UI.
 50        (name, path, language_name).eq(&(&other.name, &other.path, &other.language_name))
 51    }
 52}
 53
 54/// Declares a scope of a toolchain added by user.
 55///
 56/// When the user adds a toolchain, we give them an option to see that toolchain in:
 57/// - All of their projects
 58/// - A project they're currently in.
 59/// - Only in the subproject they're currently in.
 60#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
 61pub enum ToolchainScope {
 62    Subproject(Arc<Path>, Arc<RelPath>),
 63    Project,
 64    /// Available in all projects on this box. It wouldn't make sense to show suggestions across machines.
 65    Global,
 66}
 67
 68impl ToolchainScope {
 69    pub fn label(&self) -> &'static str {
 70        match self {
 71            ToolchainScope::Subproject(_, _) => "Subproject",
 72            ToolchainScope::Project => "Project",
 73            ToolchainScope::Global => "Global",
 74        }
 75    }
 76
 77    pub fn description(&self) -> &'static str {
 78        match self {
 79            ToolchainScope::Subproject(_, _) => {
 80                "Available only in the subproject you're currently in."
 81            }
 82            ToolchainScope::Project => "Available in all locations in your current project.",
 83            ToolchainScope::Global => "Available in all of your projects on this machine.",
 84        }
 85    }
 86}
 87
 88#[derive(Clone, PartialEq, Eq, Hash)]
 89pub struct ToolchainMetadata {
 90    /// Returns a term which we should use in UI to refer to toolchains produced by a given `ToolchainLister`.
 91    pub term: SharedString,
 92    /// A user-facing placeholder describing the semantic meaning of a path to a new toolchain.
 93    pub new_toolchain_placeholder: SharedString,
 94    /// The name of the manifest file for this toolchain.
 95    pub manifest_name: ManifestName,
 96}
 97
 98type DefaultIndex = usize;
 99#[derive(Default, Clone, Debug)]
100pub struct ToolchainList {
101    pub toolchains: Vec<Toolchain>,
102    pub default: Option<DefaultIndex>,
103    pub groups: Box<[(usize, SharedString)]>,
104}
105
106impl ToolchainList {
107    pub fn toolchains(&self) -> &[Toolchain] {
108        &self.toolchains
109    }
110    pub fn default_toolchain(&self) -> Option<Toolchain> {
111        self.default.and_then(|ix| self.toolchains.get(ix)).cloned()
112    }
113    pub fn group_for_index(&self, index: usize) -> Option<(usize, SharedString)> {
114        if index >= self.toolchains.len() {
115            return None;
116        }
117        let first_equal_or_greater = self
118            .groups
119            .partition_point(|(group_lower_bound, _)| group_lower_bound <= &index);
120        self.groups
121            .get(first_equal_or_greater.checked_sub(1)?)
122            .cloned()
123    }
124}