feat: git integration

adds View Repository button
This commit is contained in:
Jozef Steinhübl 2024-07-21 13:53:44 +02:00
parent 18daae3825
commit f1c8e5ac0b
No known key found for this signature in database
GPG key ID: E6BC90C91973B08F
5 changed files with 178 additions and 18 deletions

99
Cargo.lock generated
View file

@ -89,6 +89,10 @@ name = "cc"
version = "1.1.5" version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052"
dependencies = [
"jobserver",
"libc",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -114,6 +118,7 @@ name = "discord-presence-lsp"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"discord-rich-presence", "discord-rich-presence",
"git2",
"tokio", "tokio",
"tower-lsp", "tower-lsp",
] ]
@ -239,6 +244,21 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[package]]
name = "git2"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724"
dependencies = [
"bitflags 2.6.0",
"libc",
"libgit2-sys",
"log",
"openssl-probe",
"openssl-sys",
"url",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.5" version = "0.14.5"
@ -299,6 +319,15 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jobserver"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "leb128" name = "leb128"
version = "0.2.5" version = "0.2.5"
@ -311,6 +340,46 @@ version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]]
name = "libgit2-sys"
version = "0.17.0+1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10472326a8a6477c3c20a64547b0059e4b0d086869eee31e6d7da728a8eb7224"
dependencies = [
"cc",
"libc",
"libssh2-sys",
"libz-sys",
"openssl-sys",
"pkg-config",
]
[[package]]
name = "libssh2-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee"
dependencies = [
"cc",
"libc",
"libz-sys",
"openssl-sys",
"pkg-config",
"vcpkg",
]
[[package]]
name = "libz-sys"
version = "1.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.12" version = "0.4.12"
@ -391,6 +460,24 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "openssl-probe"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.12.3" version = "0.12.3"
@ -452,6 +539,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.86" version = "1.0.86"
@ -802,6 +895,12 @@ dependencies = [
"getrandom", "getrandom",
] ]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"

View file

@ -7,3 +7,4 @@ edition = "2021"
discord-rich-presence = "0.2.4" discord-rich-presence = "0.2.4"
tokio = { version = "1.37.0", features = ["full"] } tokio = { version = "1.37.0", features = ["full"] }
tower-lsp = "0.20.0" tower-lsp = "0.20.0"
git2 = "0.19.0"

View file

@ -4,7 +4,7 @@ use std::{
}; };
use discord_rich_presence::{ use discord_rich_presence::{
activity::{self, Assets, Timestamps}, activity::{self, Assets, Button, Timestamps},
DiscordIpc, DiscordIpcClient, DiscordIpc, DiscordIpcClient,
}; };
@ -41,10 +41,11 @@ impl Discord {
result.unwrap(); result.unwrap();
} }
pub fn change_file(&self, filename: &str, workspace: &str) { pub fn change_file(&self, filename: &str, workspace: &str, git_remote_url: Option<String>) {
self.change_activity( self.change_activity(
format!("Working on {}", filename), format!("Working on {}", filename),
format!("In {}", workspace), format!("In {}", workspace),
git_remote_url,
) )
} }
@ -52,20 +53,27 @@ impl Discord {
return self.client.lock().expect("Failed to lock discord client"); return self.client.lock().expect("Failed to lock discord client");
} }
fn change_activity(&self, state: String, details: String) { fn change_activity(&self, state: String, details: String, git_remote_url: Option<String>) {
let mut client = self.get_client(); let mut client = self.get_client();
let timestamp: i64 = self.start_timestamp.as_millis() as i64; let timestamp: i64 = self.start_timestamp.as_millis() as i64;
let mut buttons: Vec<Button> = Vec::new();
if let Some(git_remote_url) = git_remote_url.as_ref() {
buttons.push(Button::new("View Repository", git_remote_url));
}
client client
.set_activity( .set_activity(
activity::Activity::new() activity::Activity::new()
.assets(Assets::new().large_image("logo")) .assets(Assets::new().large_image("logo"))
.state(state.as_str()) .state(state.as_str())
.details(details.as_str()) .details(details.as_str())
.timestamps(Timestamps::new().start(timestamp)), .timestamps(Timestamps::new().start(timestamp))
.buttons(buttons),
) )
.unwrap_or_else(|_| { .unwrap_or_else(|_| {
panic!( println!(
"Failed to set activity with state {} and details {}", "Failed to set activity with state {} and details {}",
state, details state, details
) )

44
lsp/src/git.rs Normal file
View file

@ -0,0 +1,44 @@
use git2::Repository;
fn get_repository(path: &str) -> Option<Repository> {
match Repository::open(path) {
Ok(repo) => Some(repo),
Err(_) => None,
}
}
fn get_main_remote_url(repository: Repository) -> Option<String> {
if let Ok(remote) = repository.find_remote("origin") {
return remote.url().map(|url| transform_url(url.to_string()));
}
return match repository.remotes() {
Ok(remotes) => repository
.find_remote(remotes.get(0).unwrap())
.unwrap()
.url()
.map(|url| transform_url(url.to_string())),
Err(_) => None,
};
}
fn transform_url(url: String) -> String {
if url.starts_with("https://") {
return url;
}
if let Some((_, rest)) = url.split_once('@') {
if let Some((domain, path)) = rest.split_once(':') {
return format!("https://{}/{}", domain, path);
}
}
url.to_string()
}
pub fn get_repository_and_remote(path: &str) -> Option<String> {
match get_repository(path) {
Some(repository) => get_main_remote_url(repository),
None => None,
}
}

View file

@ -3,11 +3,13 @@ use std::path::{Path, PathBuf};
use std::sync::{Mutex, MutexGuard}; use std::sync::{Mutex, MutexGuard};
use discord::Discord; use discord::Discord;
use git::get_repository_and_remote;
use tower_lsp::jsonrpc::Result; use tower_lsp::jsonrpc::Result;
use tower_lsp::lsp_types::*; use tower_lsp::lsp_types::*;
use tower_lsp::{Client, LanguageServer, LspService, Server}; use tower_lsp::{Client, LanguageServer, LspService, Server};
mod discord; mod discord;
mod git;
#[derive(Debug)] #[derive(Debug)]
struct Document { struct Document {
@ -19,6 +21,7 @@ struct Backend {
discord: discord::Discord, discord: discord::Discord,
client: Client, client: Client,
workspace_file_name: Mutex<String>, workspace_file_name: Mutex<String>,
git_remote_url: Mutex<Option<String>>,
} }
impl Document { impl Document {
@ -42,23 +45,16 @@ impl Backend {
client, client,
discord: Discord::new(), discord: Discord::new(),
workspace_file_name: Mutex::new(String::new()), workspace_file_name: Mutex::new(String::new()),
git_remote_url: Mutex::new(None),
} }
} }
async fn on_change(&self, doc: Document) { async fn on_change(&self, doc: Document) {
self.client self.discord.change_file(
.log_message(
MessageType::LOG,
format!(
"Changing to {} in {}",
doc.get_filename(), doc.get_filename(),
self.get_workspace_file_name() self.get_workspace_file_name().as_str(),
), self.get_git_remote_url(),
) )
.await;
self.discord
.change_file(doc.get_filename(), self.get_workspace_file_name().as_str())
} }
fn get_workspace_file_name(&self) -> MutexGuard<'_, String> { fn get_workspace_file_name(&self) -> MutexGuard<'_, String> {
@ -67,6 +63,15 @@ impl Backend {
.lock() .lock()
.expect("Failed to lock workspace file name"); .expect("Failed to lock workspace file name");
} }
fn get_git_remote_url(&self) -> Option<String> {
let guard = self
.git_remote_url
.lock()
.expect("Failed to lock git remote url");
guard.clone()
}
} }
#[tower_lsp::async_trait] #[tower_lsp::async_trait]
@ -89,6 +94,9 @@ impl LanguageServer for Backend {
.expect("Failed to convert workspace file name &OsStr to &str"), .expect("Failed to convert workspace file name &OsStr to &str"),
); );
let mut git_remote_url = self.git_remote_url.lock().unwrap();
*git_remote_url = get_repository_and_remote(workspace_path.to_str().unwrap());
Ok(InitializeResult { Ok(InitializeResult {
server_info: Some(ServerInfo { server_info: Some(ServerInfo {
name: env!("CARGO_PKG_NAME").into(), name: env!("CARGO_PKG_NAME").into(),