Tidy up semantic tokens settings ui (#48581)

Kirill Bulatov created

Follow-up of https://github.com/zed-industries/zed/pull/46356

Before:
<img width="719" height="217" alt="image"
src="https://github.com/user-attachments/assets/64464318-3b84-4129-9a12-721aaa6ae953"
/>

After:
<img width="686" height="213" alt="image"
src="https://github.com/user-attachments/assets/1776ced1-4367-4ad0-9e60-c3661e07675e"
/>


Release Notes:

- N/A

Change summary

crates/settings_content/src/workspace.rs |  1 
crates/settings_ui/src/page_data.rs      | 70 +++++++++++++++----------
crates/settings_ui/src/settings_ui.rs    |  2 
3 files changed, 43 insertions(+), 30 deletions(-)

Detailed changes

crates/settings_content/src/workspace.rs 🔗

@@ -805,6 +805,7 @@ pub struct ProjectPanelIndentGuidesSettings {
     MergeFrom,
     strum::VariantArray,
     strum::VariantNames,
+    strum::EnumMessage,
 )]
 #[serde(rename_all = "snake_case")]
 pub enum SemanticTokens {

crates/settings_ui/src/page_data.rs 🔗

@@ -1,7 +1,8 @@
 use gpui::{Action as _, App};
-use settings::{LanguageSettingsContent, SettingsContent};
-use std::sync::Arc;
-use strum::IntoDiscriminant as _;
+use itertools::Itertools as _;
+use settings::{LanguageSettingsContent, SemanticTokens, SettingsContent};
+use std::sync::{Arc, OnceLock};
+use strum::{EnumMessage, IntoDiscriminant as _, VariantArray};
 use ui::IntoElement;
 
 use crate::{
@@ -68,7 +69,7 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
 }
 
 fn general_page() -> SettingsPage {
-    fn general_settings_section() -> [SettingsPageItem; 9] {
+    fn general_settings_section() -> [SettingsPageItem; 8] {
         [
             SettingsPageItem::SectionHeader("General Settings"),
             SettingsPageItem::SettingItem(SettingItem {
@@ -187,30 +188,6 @@ fn general_page() -> SettingsPage {
                 metadata: None,
                 files: USER,
             }),
-            SettingsPageItem::SettingItem(SettingItem {
-                title: "Semantic Tokens",
-                description: "If semantic tokens from language servers should be rendered.",
-                field: Box::new(SettingField {
-                    json_path: Some("semantic_tokens"),
-                    pick: |settings_content| {
-                        settings_content
-                            .project
-                            .all_languages
-                            .defaults
-                            .semantic_tokens
-                            .as_ref()
-                    },
-                    write: |settings_content, value| {
-                        settings_content
-                            .project
-                            .all_languages
-                            .defaults
-                            .semantic_tokens = value;
-                    },
-                }),
-                metadata: None,
-                files: USER,
-            }),
         ]
     }
     fn security_section() -> [SettingsPageItem; 2] {
@@ -8521,7 +8498,7 @@ fn language_settings_data() -> Box<[SettingsPageItem]> {
 /// LanguageSettings items that should be included in the "Languages & Tools" page
 /// not the "Editor" page
 fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
-    fn lsp_section() -> [SettingsPageItem; 5] {
+    fn lsp_section() -> [SettingsPageItem; 6] {
         [
             SettingsPageItem::SectionHeader("LSP"),
             SettingsPageItem::SettingItem(SettingItem {
@@ -8603,6 +8580,41 @@ fn non_editor_language_settings_data() -> Box<[SettingsPageItem]> {
                 metadata: None,
                 files: USER,
             }),
+            SettingsPageItem::SettingItem(SettingItem {
+                title: "Semantic Tokens",
+                description: {
+                    static DESCRIPTION: OnceLock<&'static str> = OnceLock::new();
+                    DESCRIPTION.get_or_init(|| {
+                        SemanticTokens::VARIANTS
+                            .iter()
+                            .filter_map(|v| {
+                                v.get_documentation().map(|doc| format!("{v:?}: {doc}"))
+                            })
+                            .join("\n")
+                            .leak()
+                    })
+                },
+                field: Box::new(SettingField {
+                    json_path: Some("languages.$(language).enable_language_server"),
+                    pick: |settings_content| {
+                        settings_content
+                            .project
+                            .all_languages
+                            .defaults
+                            .semantic_tokens
+                            .as_ref()
+                    },
+                    write: |settings_content, value| {
+                        settings_content
+                            .project
+                            .all_languages
+                            .defaults
+                            .semantic_tokens = value;
+                    },
+                }),
+                metadata: None,
+                files: USER,
+            }),
         ]
     }