Skip to content

Commit

Permalink
feat(component): go to definition
Browse files Browse the repository at this point in the history
Added go to definition for component.
  • Loading branch information
alesbrelih committed May 2, 2024
1 parent 2bb7761 commit 68221d7
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 26 deletions.
67 changes: 66 additions & 1 deletion src/gitlab_ci_ls_parser/git.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use std::{

use super::{
fs_utils::{self, FSUtils},
parser_utils, GitlabFile,
parser_utils::{self, ComponentInfo, ParserUtils},
GitlabElement, GitlabFile,
};
use log::{debug, error, info};
use reqwest::{blocking::Client, header::IF_NONE_MATCH, StatusCode, Url};
Expand All @@ -23,6 +24,10 @@ pub trait Git {
remote_files: Vec<String>,
) -> anyhow::Result<Vec<GitlabFile>>;
fn fetch_remote(&self, url: Url) -> anyhow::Result<GitlabFile>;
fn fetch_remote_component(
&self,
component_info: ComponentInfo,
) -> anyhow::Result<GitlabElement>;
}

#[allow(clippy::module_name_repetitions)]
Expand All @@ -47,6 +52,53 @@ impl GitImpl {
fs_utils,
}
}

fn clone_component_repo(repo_dest: &str, component_info: &ComponentInfo) {
let repo_dest_path = std::path::Path::new(&repo_dest);

info!("repo_path: {:?}", repo_dest_path);

if repo_dest_path.exists() {
let mut repo_contents = match repo_dest_path.read_dir() {
Ok(contents) => contents,
Err(err) => {
error!("error reading repo contents; got err: {}", err);
return;
}
};

if repo_contents.next().is_some() {
info!("repo contents exist");

return;
}
}

match Command::new("git")
.args([
"clone",
"--depth",
"1",
"--branch",
&component_info.version,
format!("git@{}:{}", component_info.host, component_info.project).as_str(),
&repo_dest,
])
.output()
{
Ok(ok) => {
info!("successfully cloned to : {}; got: {:?}", repo_dest, ok);
}
Err(err) => {
error!("error cloning to: {}, got: {:?}", repo_dest, err);

let dest = path::Path::new(repo_dest);
if dest.exists() {
fs::remove_dir_all(dest).expect("should be able to remove");
}
}
};
}
}

impl Git for GitImpl {
Expand Down Expand Up @@ -230,4 +282,17 @@ impl Git for GitImpl {
})
}
}

fn fetch_remote_component(
&self,
component_info: ComponentInfo,
) -> anyhow::Result<GitlabElement> {
// TODO: handle slashes correctly..

let repo_dest = ParserUtils::get_component_dest_dir(&self.cache_path, &component_info);
self.fs_utils.create_dir_all(&repo_dest)?;

GitImpl::clone_component_repo(repo_dest.as_str(), &component_info);
ParserUtils::get_component(&repo_dest, &component_info.component)
}
}
55 changes: 54 additions & 1 deletion src/gitlab_ci_ls_parser/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ impl LSPHandlers {
}
}
parser::PositionType::Include(info) => {
if let Some(include) = LSPHandlers::on_definition_include(info, store) {
if let Some(include) = self.on_definition_include(info, store) {
locations.push(include);
}
}
Expand Down Expand Up @@ -348,7 +348,9 @@ impl LSPHandlers {
}))
}

#[allow(clippy::too_many_lines)]
fn on_definition_include(
&self,
info: IncludeInformation,
store: &HashMap<String, String>,
) -> Option<LSPLocation> {
Expand All @@ -358,6 +360,7 @@ impl LSPHandlers {
remote: None,
remote_url: None,
basic: None,
component: None,
} => {
let local = parser_utils::ParserUtils::strip_quotes(&local.path);

Expand All @@ -368,6 +371,7 @@ impl LSPHandlers {
remote: Some(remote),
remote_url: None,
basic: None,
component: None,
} => {
let file = remote.file?;
let file = parser_utils::ParserUtils::strip_quotes(&file).trim_start_matches('/');
Expand All @@ -391,11 +395,59 @@ impl LSPHandlers {
},
})
}
IncludeInformation {
local: None,
remote: None,
remote_url: None,
basic: None,
component: Some(component),
} => {
let file = component.path.trim_matches('"').trim_matches('\'');

let component_info =
match parser_utils::ParserUtils::extract_component_from_uri(file) {
Ok(c) => c,
Err(err) => {
error!("error extracting component info from uri; got: {err}");

return None;
}
};

let repo_dest = parser_utils::ParserUtils::get_component_dest_dir(
&self.cfg.cache_path,
&component_info,
);

if let Ok(component) =
parser_utils::ParserUtils::get_component(&repo_dest, &component_info.component)
{
return store
.keys()
.find(|uri| uri.ends_with(&component.uri))
.map(|uri| LSPLocation {
uri: uri.clone(),
range: Range {
start: LSPPosition {
line: 0,
character: 0,
},
end: LSPPosition {
line: 0,
character: 0,
},
},
});
}

None
}
IncludeInformation {
local: None,
remote: None,
remote_url: Some(remote_url),
basic: None,
component: None,
} => {
let remote_url = parser_utils::ParserUtils::strip_quotes(remote_url.path.as_str());
LSPHandlers::on_definition_remote(remote_url, store)
Expand All @@ -405,6 +457,7 @@ impl LSPHandlers {
remote: None,
remote_url: None,
basic: Some(basic_url),
component: None,
} => {
let url = parser_utils::ParserUtils::strip_quotes(&basic_url.path);
if let Ok(url) = Url::parse(url) {
Expand Down
1 change: 1 addition & 0 deletions src/gitlab_ci_ls_parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ pub struct IncludeInformation {
pub remote_url: Option<Include>,
pub local: Option<Include>,
pub basic: Option<Include>,
pub component: Option<Include>,
}

#[derive(Debug, Default)]
Expand Down
38 changes: 28 additions & 10 deletions src/gitlab_ci_ls_parser/parser.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use std::collections::HashMap;

use anyhow::anyhow;
use log::{error, info, warn};
use log::{error, info};
use lsp_types::{Position, Url};
use serde::{Deserialize, Serialize};

use super::{
fs_utils, git, treesitter, GitlabElement, GitlabFile, IncludeInformation, NodeDefinition,
ParseResults, RuleReference,
fs_utils, git, parser_utils::ParserUtils, treesitter, GitlabElement, GitlabFile,
IncludeInformation, NodeDefinition, ParseResults, RuleReference,
};

#[derive(Debug, Serialize, Deserialize)]
Expand Down Expand Up @@ -40,17 +40,17 @@ struct Local {
local: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Remote {
remote: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Component {
component: String,
inputs: HashMap<String, String>,
}

#[derive(Debug, Serialize, Deserialize)]
struct Remote {
remote: String,
}

pub trait Parser {
fn get_all_extends(
&self,
Expand Down Expand Up @@ -426,8 +426,26 @@ impl Parser for ParserImpl {

self.parse_remote_files(parse_results, &remote_files);
}
IncludeItem::Component(_) => {
warn!("component handling not yet implemented.");
IncludeItem::Component(node) => {
let component_info =
match ParserUtils::extract_component_from_uri(&node.component) {
Ok(c) => c,
Err(err) => {
error!("error extracting component info from uri; got: {err}");

continue;
}
};

let gitlab_component = self.git.fetch_remote_component(component_info);
if let Ok(element) = gitlab_component {
parse_results.files.push(GitlabFile {
path: element.uri,
content: element.content.unwrap_or_default(),
});
} else {
error!("could not find gitlab component: {:?}", gitlab_component);
}
}
}
}
Expand Down
112 changes: 112 additions & 0 deletions src/gitlab_ci_ls_parser/parser_utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
use log::warn;

use super::GitlabElement;

pub struct ParserUtils {}

#[derive(Debug, PartialEq)]
pub struct ComponentInfo {
pub host: String,
pub project: String,
pub component: String,
pub version: String,
}

impl ParserUtils {
pub fn strip_quotes(value: &str) -> &str {
value.trim_matches('\'').trim_matches('"')
Expand Down Expand Up @@ -79,4 +91,104 @@ impl ParserUtils {

Some(&line[start..end])
}

pub fn get_component_dest_dir(cache_path: &str, component_info: &ComponentInfo) -> String {
let components_path = format!("{cache_path}components/");
format!(
"{}{}/{}",
components_path, component_info.project, component_info.version
)
}

// Its used because component can be in four different places
// TODO: test this
pub fn get_component(repo_dest: &str, component_name: &str) -> anyhow::Result<GitlabElement> {
let paths = vec![
format!("{}/templates/{}.yml", repo_dest, component_name),
format!("{}/templates/{}.yaml", repo_dest, component_name),
format!("{}/templates/{}/template.yml", repo_dest, component_name),
format!("{}/templates/{}/template.yaml", repo_dest, component_name),
];

for p in paths {
match std::fs::read_to_string(&p) {
Ok(content) => {
return Ok(GitlabElement {
key: format!("file://{p}"),
content: Some(content),
uri: format!("file://{p}"),
range: super::Range {
start: super::LSPPosition {
line: 0,
character: 0,
},
end: super::LSPPosition {
line: 0,
character: 0,
},
},
});
}
Err(err) => {
warn!("could not find component; path: {p}, got err: {err}");
}
};
}
Err(anyhow::anyhow!("could not find component"))
}

pub fn extract_component_from_uri(uri: &str) -> anyhow::Result<ComponentInfo> {
let mut component_parts = uri.split('/').collect::<Vec<&str>>();
if component_parts.len() < 2 {
return Err(anyhow::anyhow!(
"invalid component uri structure; got: {uri}"
));
}

let host = component_parts.remove(0);
let Some(component) = component_parts.pop() else {
return Err(anyhow::anyhow!(
"could not get last element from component uri"
));
};

let component_identificator = component.split('@').collect::<Vec<&str>>();
if component_identificator.len() != 2 {
return Err(anyhow::anyhow!(
"currently supported are only components with versions"
));
}

Ok(ComponentInfo {
host: host.to_string(),
component: component_identificator[0].to_string(),
project: component_parts.join("/"),
version: component_identificator[1].to_string(),
})
}
}

#[cfg(test)]
mod tests {
use core::panic;

use super::*;

#[test]
fn test_extract_component_from_uri() {
let component_uri = "gitlab.com/some-project/sub-project/[email protected]";
let want = ComponentInfo {
component: "component".to_string(),
version: "1.0.0".to_string(),
project: "some-project/sub-project".to_string(),
host: "gitlab.com".to_string(),
};

let got = match ParserUtils::extract_component_from_uri(component_uri) {
Ok(c) => c,
Err(err) => panic!("unable to extract; got: {err}"),
};

assert_eq!(got, want);
}
}
Loading

0 comments on commit 68221d7

Please sign in to comment.