Skip to content

Commit dbb004a

Browse files
committed
feat: general improvements and better hook snippets
1 parent 72e9563 commit dbb004a

File tree

10 files changed

+88
-26
lines changed

10 files changed

+88
-26
lines changed

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ log = "0.4.22"
1212
lsp-server = "0.7.7"
1313
lsp-types = "0.97.0"
1414
rayon = "1.10.0"
15+
regex = "1.11.1"
1516
serde = { version = "1.0.214", features = ["derive"] }
1617
serde_json = "1.0.132"
1718
structured-logger = "1.0.3"

src/document_store/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,13 @@ pub fn initialize_document_store(root_dir: String) {
3030
override_builder.add("**/*.routing.yml").unwrap();
3131
override_builder.add("**/src/**/*.php").unwrap();
3232
override_builder.add("**/core/lib/**/*.php").unwrap();
33+
// For now we don't care about interfaces at all.
34+
override_builder.add("!**/src/**/*Interface.php").unwrap();
35+
override_builder.add("!**/core/lib/**/*Interface.php").unwrap();
36+
override_builder.add("!**/Plugin/**/*.php").unwrap();
3337
override_builder.add("!vendor").unwrap();
3438
override_builder.add("!node_modules").unwrap();
39+
override_builder.add("!libraries").unwrap();
3540
builder.overrides(override_builder.build().unwrap());
3641

3742
// Find all of the documents that we are interested in parsing by walking the file tree using a

src/parser/php.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,19 @@ impl PhpParser {
3434

3535
pub fn get_token_at_position(&self, position: Position) -> Option<Token> {
3636
let tree = self.get_tree()?;
37-
let node = self.get_node_at_position(&tree, position)?;
38-
self.parse_node(node, Some(self.position_to_point(position)))
37+
let mut node = self.get_node_at_position(&tree, position)?;
38+
let point = self.position_to_point(position);
39+
40+
// Return the first "parseable" token in the parent chain.
41+
let mut parsed_node: Option<Token>;
42+
loop {
43+
parsed_node = self.parse_node(node, Some(point));
44+
if parsed_node.is_some() {
45+
break;
46+
}
47+
node = node.parent()?;
48+
}
49+
parsed_node
3950
}
4051

4152
fn get_node_at_position<'a>(&self, tree: &'a Tree, position: Position) -> Option<Node<'a>> {
@@ -61,8 +72,11 @@ impl PhpParser {
6172
match self.parse_node(node, None) {
6273
Some(token) => tokens.push(token),
6374
None => {
64-
let mut cursor = node.walk();
65-
new_nodes.append(&mut node.children(&mut cursor).collect::<Vec<Node>>());
75+
if node.child_count() > 0 {
76+
let mut cursor = node.walk();
77+
new_nodes
78+
.append(&mut node.children(&mut cursor).collect::<Vec<Node>>());
79+
}
6680
}
6781
};
6882
}
@@ -80,7 +94,7 @@ impl PhpParser {
8094
}
8195
"function_definition" => self.parse_function_definition(node),
8296
"comment" => self.parse_comment(node),
83-
_ => self.parse_node(node.parent()?, point),
97+
_ => None,
8498
}
8599
}
86100

src/parser/yaml.rs

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{usize, vec};
44
use tree_sitter::{Node, Parser, Point, Tree};
55

66
use super::tokens::{
7-
DrupalRoute, DrupalRouteDefaults, DrupalService, PhpClassName, PhpMethod, Token, TokenData
7+
DrupalRoute, DrupalRouteDefaults, DrupalService, PhpClassName, PhpMethod, Token, TokenData,
88
};
99

1010
pub struct YamlParser {
@@ -31,8 +31,19 @@ impl YamlParser {
3131

3232
pub fn get_token_at_position<'a>(&self, position: Position) -> Option<Token> {
3333
let tree = self.get_tree()?;
34-
let node = self.get_node_at_position(&tree, position)?;
35-
self.parse_node(node, Some(self.position_to_point(position)))
34+
let mut node = self.get_node_at_position(&tree, position)?;
35+
let point = self.position_to_point(position);
36+
37+
// Return the first "parseable" token in the parent chain.
38+
let mut parsed_node: Option<Token>;
39+
loop {
40+
parsed_node = self.parse_node(node, Some(point));
41+
if parsed_node.is_some() {
42+
break;
43+
}
44+
node = node.parent()?;
45+
}
46+
parsed_node
3647
}
3748

3849
fn get_node_at_position<'a>(&self, tree: &'a Tree, position: Position) -> Option<Node<'a>> {
@@ -58,8 +69,11 @@ impl YamlParser {
5869
match self.parse_node(node, None) {
5970
Some(token) => tokens.push(token),
6071
None => {
61-
let mut cursor = node.walk();
62-
new_nodes.append(&mut node.children(&mut cursor).collect::<Vec<Node>>());
72+
if node.child_count() > 0 {
73+
let mut cursor = node.walk();
74+
new_nodes
75+
.append(&mut node.children(&mut cursor).collect::<Vec<Node>>());
76+
}
6377
}
6478
};
6579
}
@@ -71,7 +85,7 @@ impl YamlParser {
7185
fn parse_node<'a>(&self, node: Node, point: Option<Point>) -> Option<Token> {
7286
match node.kind() {
7387
"block_mapping_pair" => self.parse_block_mapping_pair(node, point),
74-
_ => self.parse_node(node.parent()?, point),
88+
_ => None,
7589
}
7690
}
7791

src/server/handle_notification.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub fn handle_notification(notification: Notification) -> () {
1111
"textDocument/didOpen" => handle_text_document_did_open(notification.params),
1212
"textDocument/didChange" => handle_text_document_did_change(notification.params),
1313
"textDocument/didClose" => (),
14+
"textDocument/didSave" => (),
1415
_ => log::warn!("Unhandled notification {:?}", notification),
1516
};
1617
}

src/server/handle_request.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,28 @@ use super::handlers::completion::handle_text_document_completion;
44
use super::handlers::definition::handle_text_document_definition;
55
use super::handlers::hover::handle_text_document_hover;
66

7-
pub fn handle_request(request: Request) -> Option<Response> {
7+
pub fn handle_request(request: Request) -> Response {
88
log::trace!("Handling request: {:?}", request);
99

10-
match request.method.as_str() {
10+
let request_id = request.id.clone();
11+
let response = match request.method.as_str() {
1112
"textDocument/hover" => handle_text_document_hover(request),
1213
"textDocument/definition" => handle_text_document_definition(request),
1314
"textDocument/completion" => handle_text_document_completion(request),
1415
_ => {
1516
log::warn!("Unhandled request {:?}", request);
1617
None
1718
}
19+
};
20+
21+
// The LSP spec requires the result to be null for empty responses.
22+
match response {
23+
Some(res) => res,
24+
None => Response {
25+
id: request_id,
26+
result: Some(serde_json::Value::Null),
27+
error: None,
28+
},
1829
}
1930
}
2031

src/server/handlers/completion/mod.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use std::collections::HashMap;
22

33
use lsp_server::{ErrorCode, Request, Response};
44
use lsp_types::{
5-
CompletionItem, CompletionItemKind, CompletionList, CompletionParams, Documentation,
6-
InsertTextFormat,
5+
CompletionItem, CompletionItemKind, CompletionItemLabelDetails, CompletionList, CompletionParams, Documentation, InsertTextFormat
76
};
7+
use regex::Regex;
88

99
use crate::document_store::DOCUMENT_STORE;
1010
use crate::documentation::get_documentation_for_token;
@@ -88,8 +88,8 @@ pub fn handle_text_document_completion(request: Request) -> Option<Response> {
8888
}
8989
}
9090

91-
// TODO: Refine this.
92-
if uri.ends_with(".module") || uri.ends_with(".theme") {
91+
let (file_name, extension) = uri.split('/').last()?.split_once('.')?;
92+
if extension == "module" || extension == "theme" {
9393
DOCUMENT_STORE
9494
.lock()
9595
.unwrap()
@@ -102,14 +102,23 @@ pub fn handle_text_document_completion(request: Request) -> Option<Response> {
102102
if let Some(documentation_string) = get_documentation_for_token(token) {
103103
documentation = Some(Documentation::String(documentation_string));
104104
}
105+
// Regex to replace placeholders in hook names.
106+
let re = Regex::new(r"([A-Z][A-Z_]+[A-Z])").unwrap();
105107
completion_items.push(CompletionItem {
106108
label: hook.name.clone(),
107-
kind: Some(CompletionItemKind::FUNCTION),
109+
label_details: Some(CompletionItemLabelDetails {
110+
description: Some("hook".to_string()),
111+
detail: None,
112+
}),
113+
kind: Some(CompletionItemKind::SNIPPET),
114+
insert_text_format: Some(InsertTextFormat::SNIPPET),
108115
insert_text: Some(
109116
format!(
110-
"{}({})",
117+
"/**\n * Implements {}().\n */\nfunction {}_{}({}) {{\n $0\n}}",
111118
hook.name,
112-
hook.parameters.clone().unwrap_or("".to_string())
119+
file_name,
120+
re.replace_all(hook.name.replace("hook_", "").as_str(), r"$${$1}"),
121+
hook.parameters.clone().unwrap_or("".to_string()).replace("$", "\\$")
113122
)
114123
.to_string(),
115124
),

src/server/handlers/definition/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,17 @@ pub fn handle_text_document_definition(request: Request) -> Option<Response> {
2929
}
3030

3131
let Some(token) = token else {
32+
return Some(Response {
33+
id: request.id,
34+
result: Some(serde_json::Value::Null),
35+
error: None,
36+
});
37+
};
38+
39+
let Some(definition_result) = provide_definition_for_token(&token) else {
3240
return None;
3341
};
3442

35-
let definition_result = provide_definition_for_token(&token);
3643
return match serde_json::to_value(definition_result) {
3744
Ok(result) => Some(Response {
3845
id: request.id,

src/server/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@ async fn main_loop(connection: Connection) -> () {
2222
match msg {
2323
Message::Notification(notification) => handle_notification(notification),
2424
Message::Request(request) => {
25-
if let Some(response) = handle_request(request) {
26-
if let Err(e) = connection.sender.send(Message::Response(response)) {
27-
log::error!("Failed to send response: {:?}", e);
28-
}
25+
let response = handle_request(request);
26+
if let Err(e) = connection.sender.send(Message::Response(response)) {
27+
log::error!("Failed to send response: {:?}", e);
2928
}
3029
}
3130
_ => log::error!("Unable to process message: {:?}", msg),

0 commit comments

Comments
 (0)