Skip to content

Commit

Permalink
feat: idle support (#27)
Browse files Browse the repository at this point in the history
* feat: idle support

* feat: idle action

* fix: clippy

* refactor: cleanup
  • Loading branch information
xhyrom authored Dec 30, 2024
1 parent c0df288 commit 859e57b
Show file tree
Hide file tree
Showing 7 changed files with 430 additions and 120 deletions.
162 changes: 145 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,38 +26,166 @@ Don't forget to give at least a ⭐ if you like this project :D

You can configure state, details and git integration by changing Discord Presence LSP settings. This can be done in <kbd>zed: open settings</kbd> with following configuration:

```json
### Application ID

The `application_id` is required for the rich presence to work. It should be kept as is unless you have a specific reason to change it.

```jsonc
"application_id": "1263505205522337886"
```

### Base Icons URL

The `base_icons_url` is the base URL for all language icons. This URL points to the location where the icons are stored.

```jsonc
"base_icons_url": "https://raw.githubusercontent.com/xhyrom/zed-discord-presence/main/assets/icons/"
```

### State

The `state` option allows you to set the state message displayed in Discord. The placeholder `{filename}` will be replaced with the current file name.

```jsonc
"state": "Working on {filename}"
```

### Details

The `details` option allows you to set the details message displayed in Discord. The placeholder `{workspace}` will be replaced with the current workspace name.

```jsonc
"details": "In {workspace}"
```

### Large Image

The `large_image` option specifies the URL for the large image displayed in Discord. The placeholders `{base_icons_url}` and `{language}` will be replaced accordingly.

```jsonc
"large_image": "{base_icons_url}/{language}.png"
```

### Large Text

The `large_text` option specifies the text displayed when hovering over the large image. The `:u` modifier capitalizes the first letter of the language name.

```jsonc
"large_text": "{language:u}"
```

### Small Image

The `small_image` option specifies the URL for the small image displayed in Discord.

```jsonc
"small_image": "{base_icons_url}/zed.png"
```

### Small Text

The `small_text` option specifies the text displayed when hovering over the small image.

```jsonc
"small_text": "Zed"
```

### Idle Settings

The `idle` settings configure the behavior when you are inactive.

The `timeout` specifies the idle timeout in seconds (300 seconds = 5 minutes).

The `action` determines what happens when you go idle:

- `change_activity` changes the activity to idle with the specified details
- `clear_activity` hides the activity

The `state`, `details`, `large_image`, `large_text`, `small_image`, and `small_text` options specify the messages and images to display when idle.

```jsonc
"idle": {
"timeout": 300,
"action": "change_activity",
"state": "Idling",
"details": "In Zed",
"large_image": "{base_icons_url}/zed.png",
"large_text": "Zed",
"small_image": "{base_icons_url}/idle.png",
"small_text": "Idle"
}
```

### Rules

The `rules` option allows you to disable presence in specific workspaces. The `mode` can be set to `blacklist`
or `whitelist`, and the `paths` array should contain the absolute paths to apply the rule to.

```jsonc
"rules": {
"mode": "blacklist",
"paths": ["absolute path"]
}
```

### Git Integration

The `git_integration` option enables or disables Git integration. When enabled, the extension
will display a button to open the Git repository.

```jsonc
"git_integration": true
```

### Example Configuration

```jsonc
{
"lsp": {
"discord_presence": {
"initialization_options": {
// application id for the rich presence (required, keep it if you don't know what you're doing)
"application_id": "1263505205522337886"
// Base url for all language icons
// Application ID for the rich presence (don't touch it unless you know what you're doing)
"application_id": "1263505205522337886",
// Base URL for all language icons
"base_icons_url": "https://raw.githubusercontent.com/xhyrom/zed-discord-presence/main/assets/icons/",

"state": "Working on {filename}",
"details": "In {workspace}",
// URL for large image
// URL for the large image
"large_image": "{base_icons_url}/{language}.png",
"large_text": "{language:u}", // :u makes first letter upper-case
// URL for small image
"large_text": "{language:u}", // :u capitalizes the first letter
// URL for the small image
"small_image": "{base_icons_url}/zed.png",
"small_text": "Zed",

// Rules - disable presence in some workspaces
// Idle settings - when you're inactive
"idle": {
"timeout": 300, // Idle timeout in seconds (300 seconds = 5 minutes)

// Action to take when idle
// `change_activity` - changes the activity to idle with the following details
// `clear_activity` - clears the activity (hides it)
"action": "change_activity",

"state": "Idling",
"details": "In Zed",
"large_image": "{base_icons_url}/zed.png",
"large_text": "Zed",
"small_image": "{base_icons_url}/idle.png",
"small_text": "Idle",
},

// Rules to disable presence in specific workspaces
"rules": {
"mode": "blacklist", // or whitelist
"paths": [
"absolute path"
]
"mode": "blacklist", // Can also be "whitelist"
"paths": ["absolute path"],
},

"git_integration": true
}
}
}
"git_integration": true,
},
},
},
}
```

You can also use `null` to unset the option. Possible for everything except `base_icons_url`, `rules` and `git_integration`
You can also set any option to `null` to unset it, except for `base_icons_url`, `rules`, and `git_integration`.
2 changes: 1 addition & 1 deletion lsp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"

[dependencies]
discord-rich-presence = "0.2.4"
tokio = { version = "1.37.0", features = ["rt-multi-thread", "io-std", "macros"] }
tokio = { version = "1.37.0", features = ["rt-multi-thread", "io-std", "macros", "time"] }
tower-lsp = "0.20.0"
git2 = { version = "0.19.0", default-features = false }
serde_json = { version = "1.0.122", features = ["std"] }
Expand Down
59 changes: 59 additions & 0 deletions lsp/src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,43 @@ impl Rules {
}
}

#[derive(Debug, PartialEq)]
pub enum IdleAction {
ClearActivity, // Clear the activity
ChangeActivity, // Change the activity
}

#[derive(Debug)]
pub struct Idle {
pub timeout: u64, // in seconds
pub action: IdleAction, // what to do when idle

pub state: Option<String>,
pub details: Option<String>,

pub large_image: Option<String>,
pub large_text: Option<String>,
pub small_image: Option<String>,
pub small_text: Option<String>,
}

impl Default for Idle {
fn default() -> Self {
Idle {
timeout: 300,
action: IdleAction::ChangeActivity,

state: Some("Idling".to_string()),
details: Some("In Zed".to_string()),

large_image: Some(String::from("{base_icons_url}/zed.png")),
large_text: Some(String::from("Zed")),
small_image: Some(String::from("{base_icons_url}/idle.png")),
small_text: Some(String::from("Idle")),
}
}
}

#[derive(Debug)]
pub struct Configuration {
pub application_id: String,
Expand All @@ -67,6 +104,8 @@ pub struct Configuration {

pub rules: Rules,

pub idle: Idle,

pub git_integration: bool,
}

Expand Down Expand Up @@ -104,6 +143,7 @@ impl Configuration {
small_image: Some(String::from("{base_icons_url}/zed.png")),
small_text: Some(String::from("Zed")),
rules: Rules::default(),
idle: Idle::default(),
git_integration: true,
}
}
Expand Down Expand Up @@ -141,6 +181,25 @@ impl Configuration {
});
}

if let Some(idle) = options.get("idle") {
self.idle.timeout = idle.get("timeout").and_then(|t| t.as_u64()).unwrap_or(300);
self.idle.action = idle.get("action").and_then(|a| a.as_str()).map_or(
IdleAction::ChangeActivity,
|action| match action {
"clear_activity" => IdleAction::ClearActivity,
"change_activity" => IdleAction::ChangeActivity,
_ => IdleAction::ChangeActivity,
},
);

set_option!(self, idle, state, "state");
set_option!(self, idle, details, "details");
set_option!(self, idle, large_image, "large_image");
set_option!(self, idle, large_text, "large_text");
set_option!(self, idle, small_image, "small_image");
set_option!(self, idle, small_text, "small_text");
}

if let Some(git_integration) = options.get("git_integration") {
self.git_integration = git_integration.as_bool().unwrap_or(true);
}
Expand Down
29 changes: 17 additions & 12 deletions lsp/src/discord.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/

use std::{
sync::{Mutex, MutexGuard},
time::{Duration, SystemTime, UNIX_EPOCH},
};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use tokio::sync::{Mutex, MutexGuard};

use discord_rich_presence::{
activity::{Activity, Assets, Button, Timestamps},
Expand Down Expand Up @@ -55,28 +53,35 @@ impl Discord {
self.client = Some(Mutex::new(discord_client));
}

pub fn connect(&self) {
let mut client = self.get_client();
pub async fn connect(&self) {
let mut client = self.get_client().await;
let result = client.connect();
result.unwrap();
}

pub fn kill(&self) {
let mut client = self.get_client();
pub async fn kill(&self) {
let mut client = self.get_client().await;
let result = client.close();
result.unwrap();
}

pub fn get_client(&self) -> MutexGuard<'_, DiscordIpcClient> {
pub async fn get_client(&self) -> MutexGuard<'_, DiscordIpcClient> {
self.client
.as_ref()
.expect("Discord client not initialized")
.lock()
.expect("Failed to lock discord client")
.await
}

pub async fn clear_activity(&self) {
let mut client = self.get_client().await;
client
.clear_activity()
.unwrap_or_else(|_| println!("Failed to clear activity"));
}

#[allow(clippy::too_many_arguments)]
pub fn change_activity(
pub async fn change_activity(
&self,
state: Option<String>,
details: Option<String>,
Expand All @@ -86,7 +91,7 @@ impl Discord {
small_text: Option<String>,
git_remote_url: Option<String>,
) {
let mut client = self.get_client();
let mut client = self.get_client().await;
let timestamp: i64 = self.start_timestamp.as_millis() as i64;

let activity = Activity::new()
Expand Down
4 changes: 2 additions & 2 deletions lsp/src/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ fn get_main_remote_url(repository: Repository) -> Option<String> {
return remote.url().map(|url| transform_url(url.to_string()));
}

return match repository.remotes() {
match repository.remotes() {
Ok(remotes) => remotes.get(0).and_then(|name| {
repository
.find_remote(name)
.ok()
.and_then(|remote| remote.url().map(|url| transform_url(url.to_string())))
}),
Err(_) => None,
};
}
}

fn transform_url(url: String) -> String {
Expand Down
Loading

0 comments on commit 859e57b

Please sign in to comment.