Skip to content

Commit

Permalink
feat: jump to def for variables
Browse files Browse the repository at this point in the history
It lists all variables in jobs it founds when traveling through the
extends.
  • Loading branch information
alesbrelih committed Apr 5, 2024
1 parent 1f21420 commit f9185d3
Show file tree
Hide file tree
Showing 5 changed files with 274 additions and 5 deletions.
32 changes: 32 additions & 0 deletions src/gitlab_ci_ls_parser/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,38 @@ impl LSPHandlers {
}
}
}
parser::PositionType::Variable => {
let line = document.lines().nth(position.line as usize)?;
let word =
parser_utils::ParserUtils::extract_variable(line, position.character as usize)?;

let variable_locations = self.parser.get_variable_definitions(
word,
document_uri.as_str(),
position,
store,
)?;

for location in variable_locations {
locations.push(LSPLocation {
uri: location.uri,
range: location.range,
});
}
let mut root = self
.variables
.lock()
.unwrap()
.iter()
.filter(|(name, _)| name.starts_with(word))
.map(|(_, el)| LSPLocation {
uri: el.uri.clone(),
range: el.range.clone(),
})
.collect::<Vec<LSPLocation>>();

locations.append(&mut root);
}
_ => {
error!("invalid position type for goto def");
return None;
Expand Down
6 changes: 3 additions & 3 deletions src/gitlab_ci_ls_parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ pub mod parser;
pub mod parser_utils;
pub mod treesitter;

#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct LSPPosition {
pub line: u32,
pub character: u32,
}

#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct Range {
pub start: LSPPosition,
pub end: LSPPosition,
Expand Down Expand Up @@ -79,7 +79,7 @@ pub struct GitlabFile {
pub content: String,
}

#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct GitlabElement {
pub key: String,
pub content: Option<String>,
Expand Down
75 changes: 75 additions & 0 deletions src/gitlab_ci_ls_parser/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ pub trait Parser {
_follow: bool,
iteration: i32,
) -> Option<()>;
fn get_variable_definitions(
&self,
word: &str,
uri: &str,
position: Position,
store: &HashMap<String, String>,
) -> Option<Vec<GitlabElement>>;
}

#[allow(clippy::module_name_repetitions)]
Expand Down Expand Up @@ -65,6 +72,45 @@ impl ParserImpl {
}
}

fn all_nodes(
&self,
store: &HashMap<String, String>,
all_nodes: &mut Vec<GitlabElement>,
node: GitlabElement,
iter: usize,
) {
// Another safety wow
if iter > 5 {
return;
}

all_nodes.push(node.clone());

let extends =
self.get_all_extends(node.uri, node.content.unwrap_or_default().as_str(), None);

if extends.is_empty() {
return;
}

for extend in extends {
for (uri, content) in store {
if let Some(root_node) = self.get_root_node(uri, content, extend.key.as_str()) {
let node = GitlabElement {
uri: root_node.uri,
key: root_node.key,
content: Some(root_node.content.unwrap()),
..Default::default()
};

self.all_nodes(store, all_nodes, node, iter + 1);

break;
}
}
}
}

fn parse_remote_files(&self, parse_results: &mut ParseResults, remote_files: &[GitlabFile]) {
for remote_file in remote_files {
parse_results.nodes.append(
Expand Down Expand Up @@ -302,4 +348,33 @@ impl Parser for ParserImpl {
) -> Vec<GitlabElement> {
self.treesitter.get_all_job_needs(uri, content, needs_name)
}

fn get_variable_definitions(
&self,
variable: &str,
uri: &str,
position: Position,
store: &HashMap<String, String>,
) -> Option<Vec<GitlabElement>> {
let mut all_nodes = vec![];

if let Some(content) = store.get(uri) {
let element = self
.treesitter
.get_root_node_at_position(content, position)?;

self.all_nodes(store, &mut all_nodes, element, 0);
}

Some(
all_nodes
.iter()
.filter_map(|e| {
let cnt = store.get(&e.uri)?;
self.treesitter
.job_variable_definition(e.uri.as_str(), cnt, variable, &e.key)
})
.collect(),
)
}
}
16 changes: 16 additions & 0 deletions src/gitlab_ci_ls_parser/parser_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,20 @@ impl ParserUtils {
uri.hash(&mut hasher);
hasher.finish().to_string()
}

pub fn extract_variable(line: &str, char_index: usize) -> Option<&str> {
if char_index >= line.len() {
return None;
}

let start = line[..char_index]
.rfind(|c: char| c == '$' || c == '{')
.map_or(0, |index| index + 1);

let end = line[char_index..]
.find(|c: char| !c.is_alphabetic() && c != '_')
.map_or(line.len(), |index| index + char_index);

Some(&line[start..end])
}
}
150 changes: 148 additions & 2 deletions src/gitlab_ci_ls_parser/treesitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ pub trait Treesitter {
needs_name: Option<&str>,
) -> Vec<GitlabElement>;
fn get_position_type(&self, content: &str, position: Position) -> parser::PositionType;
fn get_root_node_at_position(&self, content: &str, position: Position)
-> Option<GitlabElement>;
fn job_variable_definition(
&self,
uri: &str,
content: &str,
variable_name: &str,
job_name: &str,
) -> Option<GitlabElement>;
}

#[allow(clippy::module_name_repetitions)]
Expand Down Expand Up @@ -694,8 +703,6 @@ impl Treesitter for TreesitterImpl {
// together but there are multiple capture groups I need to iterate over
// TODO: check treesitter if I can group to make this easier.. Perhaps some capture
// group is wrong.
error!("CAPTURES: {:?}", mat.captures);
error!("position: {:?}", position.line);
let remote_include_indexes: Vec<u32> = vec![10, 11, 12, 13, 14, 15, 16, 17];
if mat
.captures
Expand Down Expand Up @@ -872,4 +879,143 @@ impl Treesitter for TreesitterImpl {

needs
}

fn get_root_node_at_position(
&self,
content: &str,
position: Position,
) -> Option<GitlabElement> {
let mut parser = tree_sitter::Parser::new();
parser
.set_language(tree_sitter_yaml::language())
.expect("Error loading YAML grammar");

let query_source = r"
(
stream(
document(
block_node(
block_mapping(
block_mapping_pair
key: (flow_node(plain_scalar(string_scalar)@key))
)@full
)
)
)
)
";

let tree = parser.parse(content, None).unwrap();
let root_node = tree.root_node();

let query = Query::new(language(), query_source).unwrap();

let mut cursor_qry = QueryCursor::new();
let matches = cursor_qry.matches(&query, root_node, content.as_bytes());

matches
.into_iter()
.flat_map(|m| m.captures.iter())
.find(|c| {
c.index == 1
&& c.node.start_position().row <= position.line as usize
&& c.node.end_position().row >= position.line as usize
})
.map(|c| {
let text = content[c.node.byte_range()].to_string();
let key = text.lines().collect::<Vec<&str>>()[0]
.trim_end_matches(':')
.to_string();

GitlabElement {
key,
content: Some(text),
..Default::default()
}
})
}

fn job_variable_definition(
&self,
uri: &str,
content: &str,
variable_name: &str,
job_name: &str,
) -> Option<GitlabElement> {
let mut parser = tree_sitter::Parser::new();
parser
.set_language(tree_sitter_yaml::language())
.expect("Error loading YAML grammar");

let query_source = format!(
r#"
(
stream(
document(
block_node(
block_mapping(
block_mapping_pair
key: (flow_node(plain_scalar(string_scalar)@key))
value: (
block_node(
block_mapping(
block_mapping_pair
key: (flow_node(plain_scalar(string_scalar)@property_key))
value: (
block_node(
block_mapping(
block_mapping_pair
key: (flow_node(plain_scalar(string_scalar)@variable_key))
)
)
)
(#eq? @property_key "variables")
)
)
)
)
)
)
)
(#eq? @key "{job_name}")
(#eq? @variable_key "{variable_name}")
)
"#
);

let tree = parser.parse(content, None).unwrap();
let root_node = tree.root_node();

let query = Query::new(language(), &query_source).unwrap();

let mut cursor_qry = QueryCursor::new();
let matches = cursor_qry.matches(&query, root_node, content.as_bytes());

matches
.into_iter()
.flat_map(|m| m.captures.iter())
.find(|c| c.index == 2)
.map(|c| {
let text = content[c.node.byte_range()].to_string();
let key = text.lines().collect::<Vec<&str>>()[0]
.trim_end_matches(':')
.to_string();

GitlabElement {
uri: uri.to_string(),
key,
content: Some(text),
range: Range {
start: LSPPosition {
line: u32::try_from(c.node.start_position().row).unwrap_or(0),
character: u32::try_from(c.node.start_position().column).unwrap_or(0),
},
end: LSPPosition {
line: u32::try_from(c.node.end_position().row).unwrap_or(0),
character: u32::try_from(c.node.end_position().column).unwrap_or(0),
},
},
}
})
}
}

0 comments on commit f9185d3

Please sign in to comment.