From 399dcc70afa7d54ea98ca3dcb9d96ace50e46a22 Mon Sep 17 00:00:00 2001 From: Uros Mrkobrada Date: Tue, 12 Mar 2024 21:20:02 +0100 Subject: [PATCH] added completion for included templates --- Cargo.lock | 4 +- jinja-lsp-queries/Cargo.toml | 2 +- jinja-lsp-queries/src/capturer/included.rs | 122 ++++++++- jinja-lsp-queries/src/capturer/object.rs | 4 +- jinja-lsp-queries/src/queries.rs | 30 ++- jinja-lsp-queries/src/test_queries.rs | 11 + jinja-lsp/Cargo.toml | 4 +- jinja-lsp/src/channels/lsp.rs | 7 + jinja-lsp/src/config.rs | 1 - jinja-lsp/src/filter/md/filters/some.sh | 40 --- jinja-lsp/src/filter/md/filters/some2.sh | 7 - jinja-lsp/src/filter/md/filters/some3.sh | 284 --------------------- jinja-lsp/src/filter/md/filters/some4.sh | 74 ------ jinja-lsp/src/lsp_files.rs | 52 +++- 14 files changed, 206 insertions(+), 436 deletions(-) delete mode 100644 jinja-lsp/src/filter/md/filters/some.sh delete mode 100644 jinja-lsp/src/filter/md/filters/some2.sh delete mode 100644 jinja-lsp/src/filter/md/filters/some3.sh delete mode 100644 jinja-lsp/src/filter/md/filters/some4.sh diff --git a/Cargo.lock b/Cargo.lock index 0ab5697..a040a87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -290,7 +290,7 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jinja-lsp" -version = "0.1.6" +version = "0.1.61" dependencies = [ "anyhow", "env_logger", @@ -308,7 +308,7 @@ dependencies = [ [[package]] name = "jinja-lsp-queries" -version = "0.1.6" +version = "0.1.61" dependencies = [ "ropey", "tower-lsp", diff --git a/jinja-lsp-queries/Cargo.toml b/jinja-lsp-queries/Cargo.toml index d740238..b7b42a5 100644 --- a/jinja-lsp-queries/Cargo.toml +++ b/jinja-lsp-queries/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jinja-lsp-queries" -version = "0.1.6" +version = "0.1.61" edition = "2021" description = "TreeSitter queries for jinja-lsp" license = "MIT" diff --git a/jinja-lsp-queries/src/capturer/included.rs b/jinja-lsp-queries/src/capturer/included.rs index 28537e2..18ebe44 100644 --- a/jinja-lsp-queries/src/capturer/included.rs +++ b/jinja-lsp-queries/src/capturer/included.rs @@ -1,7 +1,7 @@ -use tower_lsp::lsp_types::Url; +use tower_lsp::lsp_types::{Position, Range, Url}; use tree_sitter::Point; -use super::Capturer; +use super::{object::CompletionType, Capturer}; #[derive(Default, Debug)] pub struct IncludeCapturer { @@ -11,15 +11,35 @@ pub struct IncludeCapturer { impl IncludeCapturer { pub fn in_template(&self, trigger_point: Point) -> Option<&IncludedTemplate> { if let Some(last) = self.included.last() { - if trigger_point >= last.range.0 && trigger_point <= last.range.1 { + if trigger_point >= last.template.0 && trigger_point <= last.template.1 { return Some(last); } } None } + pub fn find(&self, trigger_point: Point) -> Option<(&IncludedTemplate, Include)> { + if let Some(string) = self.in_template(trigger_point) { + return Some((string, Include::Template)); + } + None + } + + pub fn completion(&self, trigger_point: Point) -> Option { + let part = self.find(trigger_point)?; + let location = part.1.location(trigger_point, part.0); + Some(CompletionType::IncludedTemplate { + name: location.0, + range: location.1, + }) + } + pub fn add_template(&mut self, name: String, range: (Point, Point)) { - self.included.push(IncludedTemplate { name, range }); + self.included.push(IncludedTemplate { + name, + template: range, + ..Default::default() + }); } pub fn last(&self) -> Option<&String> { @@ -35,12 +55,33 @@ impl Capturer for IncludeCapturer { source: &str, ) { let key = capture_names[capture.index as usize].to_owned(); - if key == "template" { - if let Ok(value) = capture.node.utf8_text(source.as_bytes()) { + if key == "keyword" { + self.add_template("".to_owned(), (Point::default(), Point::default())); + } else if key == "error" { + if let Some(last) = self.included.last_mut() { + let start = capture.node.start_position(); + let end = capture.node.end_position(); + last.error = (start, end); + } + } else if key == "id" { + if let Some(last) = self.included.last_mut() { let start = capture.node.start_position(); let end = capture.node.end_position(); - let name = value.replace(['\'', '\"'], ""); - self.add_template(name, (start, end)); + last.identifier = (start, end); + if let Ok(value) = capture.node.utf8_text(source.as_bytes()) { + let name = value.replace(['\'', '\"'], ""); + last.name = name; + } + } + } else if key == "template" { + if let Ok(value) = capture.node.utf8_text(source.as_bytes()) { + if let Some(last) = self.included.last_mut() { + let start = capture.node.start_position(); + let end = capture.node.end_position(); + let name = value.replace(['\'', '\"'], ""); + last.name = name; + last.template = (start, end); + } } } } @@ -49,14 +90,17 @@ impl Capturer for IncludeCapturer { #[derive(Default, Debug)] pub struct IncludedTemplate { pub name: String, - pub range: (Point, Point), + pub template: (Point, Point), + pub error: (Point, Point), + pub identifier: (Point, Point), } impl IncludedTemplate { pub fn new(name: &str) -> Self { Self { name: name.to_owned(), - range: (Point::default(), Point::default()), + template: (Point::default(), Point::default()), + ..Default::default() } } @@ -68,3 +112,61 @@ impl IncludedTemplate { Some(uri) } } + +#[derive(Debug)] +pub enum Include { + Id, + Template, + Error, +} + +impl Include { + pub fn location(&self, trigger_point: Point, part: &IncludedTemplate) -> (String, Range) { + match self { + Include::Error => { + let range = self.to_range(part.error); + (String::from(""), range) + } + Include::Id => { + let l1 = part.identifier.1.column - trigger_point.column; + if part.name.len() < l1 { + let range = self.to_range(part.identifier); + return (String::from(""), range); + } + let end = part.name.len() - l1; + let mut name = String::new(); + for (i, item) in part.name.char_indices() { + name.push(item); + if i == end { + break; + } + } + let range = self.to_range(part.identifier); + (name, range) + } + Include::Template => { + let l1 = part.template.1.column - trigger_point.column; + if part.name.len() < l1 || part.name.is_empty() { + let range = self.to_range(part.template); + return (String::from(""), range); + } + let end = part.name.len() - l1; + let mut name = String::new(); + for (i, item) in part.name.char_indices() { + name.push(item); + if i == end { + break; + } + } + let range = self.to_range(part.template); + (name, range) + } + } + } + + pub fn to_range(&self, points: (Point, Point)) -> Range { + let start = Position::new(points.0.row as u32, points.0.column as u32); + let end = Position::new(points.1.row as u32, points.1.column as u32); + Range::new(start, end) + } +} diff --git a/jinja-lsp-queries/src/capturer/object.rs b/jinja-lsp-queries/src/capturer/object.rs index ffa0d41..4f167b6 100644 --- a/jinja-lsp-queries/src/capturer/object.rs +++ b/jinja-lsp-queries/src/capturer/object.rs @@ -1,3 +1,4 @@ +use tower_lsp::lsp_types::Range; use tree_sitter::{Point, QueryCapture}; use super::Capturer; @@ -134,10 +135,11 @@ impl JinjaObject { } } -#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] +#[derive(PartialEq, Debug)] pub enum CompletionType { Filter, Identifier, + IncludedTemplate { name: String, range: Range }, } static VALID_IDENTIFIERS: [&str; 4] = ["loop", "true", "false", "not"]; diff --git a/jinja-lsp-queries/src/queries.rs b/jinja-lsp-queries/src/queries.rs index 3fae8e6..b8ed8ca 100644 --- a/jinja-lsp-queries/src/queries.rs +++ b/jinja-lsp-queries/src/queries.rs @@ -125,16 +125,22 @@ pub static RUST: &str = r#" "#; pub static IMPORTS: &str = r#" -( - [ - (statement - (statement_begin) - (keyword) @keyword - (string) @template - (statement_end) - - (#eq? @keyword "include") - ) @included - ] -) +[ + (statement + (statement_begin) + (keyword) @keyword + (string)? @template + (ERROR)? @error + (identifier)? @id + (statement_end)? @end + (#eq? @keyword "include") + ) + (statement_begin) + (keyword) @keyword + (string)? @template + (ERROR)? @error + (identifier)? @id + (statement_end)? @end + (#eq? @keyword "include") +] "#; diff --git a/jinja-lsp-queries/src/test_queries.rs b/jinja-lsp-queries/src/test_queries.rs index ce10c0b..ad94e24 100644 --- a/jinja-lsp-queries/src/test_queries.rs +++ b/jinja-lsp-queries/src/test_queries.rs @@ -227,4 +227,15 @@ mod query_tests { assert_eq!(&template.unwrap().name, &case.1); } } + + // #[test] + // fn included_template_completion() { + // let source = r#" + //
+ // {% include " + //
+ // "#; + + // let cases = [(Point::new(2, 28), false, true, false)]; + // } } diff --git a/jinja-lsp/Cargo.toml b/jinja-lsp/Cargo.toml index 66572b8..c810992 100644 --- a/jinja-lsp/Cargo.toml +++ b/jinja-lsp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jinja-lsp" -version = "0.1.6" +version = "0.1.61" edition = "2021" license = "MIT" authors = ["uros-5"] @@ -30,4 +30,4 @@ walkdir = "2.4.0" anyhow = "1.0.75" tree-sitter-jinja2 = "0.0.5" tree-sitter-rust = "0.20.4" -jinja-lsp-queries = { path = "../jinja-lsp-queries", version = "0.1.6"} +jinja-lsp-queries = { path = "../jinja-lsp-queries", version = "0.1.61"} diff --git a/jinja-lsp/src/channels/lsp.rs b/jinja-lsp/src/channels/lsp.rs index 3a39e68..28751cb 100644 --- a/jinja-lsp/src/channels/lsp.rs +++ b/jinja-lsp/src/channels/lsp.rs @@ -183,6 +183,13 @@ pub fn lsp_task( items = Some(CompletionResponse::Array(variables)); } } + CompletionType::IncludedTemplate { name, range } => { + if let Some(templates) = + lsp_data.read_templates(name, &config, range) + { + items = Some(CompletionResponse::Array(templates)); + } + } }; } let _ = sender.send(items); diff --git a/jinja-lsp/src/config.rs b/jinja-lsp/src/config.rs index 6124986..c0fe4a3 100644 --- a/jinja-lsp/src/config.rs +++ b/jinja-lsp/src/config.rs @@ -41,7 +41,6 @@ pub type InitLsp = ( ); pub fn walkdir(config: &JinjaConfig) -> anyhow::Result { - // let mut all = vec![config.templates.clone()]; let mut backend = config.backend.clone(); all.append(&mut backend); diff --git a/jinja-lsp/src/filter/md/filters/some.sh b/jinja-lsp/src/filter/md/filters/some.sh deleted file mode 100644 index 5fc1b2e..0000000 --- a/jinja-lsp/src/filter/md/filters/some.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -echo "abs.md\nReturns the absolute value of a number." > abs.md -echo "attr.md\nLooks up an attribute." > attr.md -echo "batch.md\nBatch items." > batch.md -echo "bool.md\nConverts the value into a boolean value." > bool.md -echo "capitalize.md\nConvert the string with all its characters lowercased apart from the first char which is uppercased." > capitalize.md -echo "default.md\nIf the value is undefined it will return the passed default value, otherwise the value of the variable:" > default.md -echo "dictsort.md\nDict sorting functionality." > dictsort.md -echo "escape.md\nEscapes a string. By default to HTML." > escape.md -echo "first.md\nReturns the first item from a list." > first.md -echo "float.md\nConverts a value into a float." > float.md -echo "indent.md\nIndents Value with spaces." > indent.md -echo "int.md\nConverts a value into an integer." > int.md -echo "items.md\nReturns a list of pairs (items) from a mapping." > items.md -echo "join.md\nJoins a sequence by a character" > join.md -echo "last.md\nReturns the last item from a list." > last.md -echo "length.md\nReturns the “length” of the value" > length.md -echo "list.md\nConverts the input value into a list." > list.md -echo "lower.md\nConverts a value to lowercase." > lower.md -echo "map.md\nApplies a filter to a sequence of objects or looks up an attribute." > map.md -echo "max.md\nReturns the largest item from the list." > max.md -echo "min.md\nReturns the smallest item from the list." > min.md -echo "pprint.md\nPretty print a variable." > pprint.md -echo "reject.md\nCreates a new sequence of values that don’t pass a test." > reject.md -echo "rejectattr.md\nCreates a new sequence of values of which an attribute does not pass a test." > rejectattr.md -echo "replace.md\nDoes a string replace." > replace.md -echo "reverse.md\nReverses a list or string" > reverse.md -echo "round.md\nRound the number to a given precision." > round.md -echo "safe.md\nMarks a value as safe. This converts it into a string." > safe.md -echo "select.md\nCreates a new sequence of values that pass a test." > select.md -echo "selectattr.md\nCreates a new sequence of values of which an attribute passes a test." > selectattr.md -echo "slice.md\nSlice an iterable and return a list of lists containing those items." > slice.md -echo "sort.md\nReturns the sorted version of the given list." > sort.md -echo "title.md\nConverts a value to title case." > title.md -echo "tojson.md\nDumps a value to JSON." > tojson.md -echo "trim.md\nTrims a value" > trim.md -echo "unique.md\nReturns a list of unique items from the given iterable." > unique.md -echo "upper.md\nConverts a value to uppercase." > upper.md -echo "urlencode.md\nURL encodes a value." > urlencode.md diff --git a/jinja-lsp/src/filter/md/filters/some2.sh b/jinja-lsp/src/filter/md/filters/some2.sh deleted file mode 100644 index 5a10943..0000000 --- a/jinja-lsp/src/filter/md/filters/some2.sh +++ /dev/null @@ -1,7 +0,0 @@ - -echo "dateformat\nFormats a timestamp as date." > contrib/dateformat.md -echo "datetimeformat\nFormats a timestamp as date and time." > contrib/datetimeformat.md -echo "pluralize\nReturns a plural suffix if the value is not 1, ‘1’, or an object of length 1." > contrib/pluralize.md -echo "timeformat\nFormats a timestamp as time." > contrib/timeformat.md - - diff --git a/jinja-lsp/src/filter/md/filters/some3.sh b/jinja-lsp/src/filter/md/filters/some3.sh deleted file mode 100644 index 6fe9d03..0000000 --- a/jinja-lsp/src/filter/md/filters/some3.sh +++ /dev/null @@ -1,284 +0,0 @@ -#!/bin/sh - -echo 'safe\n Marks a value as safe. This converts it into a string. -\n When a value is marked as safe, no further auto escaping will take place. -' > safe.md - -echo 'escape\n Escapes a string. By default to HTML. -\n By default this filter is also registered under the alias `e`. Note that -\n this filter escapes with the format that is native to the format or HTML -\n otherwise. This means that if the auto escape setting is set to -\n `Json` for instance then this filter will serialize to JSON instead. -' > escape.md - - -echo 'upper\n Converts a value to uppercase. -\n ```jinja -\n

{{ chapter.title|upper }}

-\n ``` -' > upper.md - -echo 'lower\n Converts a value to lowercase. -\n ```jinja -\n

{{ chapter.title|lower }}

-\n ``` -' > lower.md - -echo 'title\n Converts a value to title case. -\n ```jinja -\n

{{ chapter.title|title }}

-\n ``` -' > title.md - -echo 'capitalize\n Convert the string with all its characters lowercased -\n apart from the first char which is uppercased. -\n ```jinja -\n

{{ chapter.title|capitalize }}

-\n ``` -' > capitalize.md - -echo 'replace -\n Does a string replace. -\n It replaces all occurrences of the first parameter with the second. -\n ```jinja -\n {{ "Hello World"|replace("Hello", "Goodbye") }} -\n -> Goodbye World -\n ``` -' > replace.md - - -echo 'length\n Returns the "length" of the value -\n By default this filter is also registered under the alias `count`. -\n ```jinja -\n

Search results: {{ results|length }} -\n ``` -' > length.md - -echo 'dictsort\n \n Dict sorting functionality. -\n This filter works like `|items` but sorts the pairs by key first. -\n The filter accepts a few keyword arguments: -\n * `case_sensitive`: set to `true` to make the sorting of strings case sensitive. -\n * `by`: set to `"value"` to sort by value. Defaults to `"key"`. -\n * `reverse`: set to `true` to sort in reverse. -' > dictsort.md - -echo 'items\n Returns a list of pairs (items) from a mapping. -\n This can be used to iterate over keys and values of a mapping -\n at once. Note that this will use the original order of the map -\n which is typically arbitrary unless the `preserve_order` feature -\n is used in which case the original order of the map is retained. -\n It s generally better to use `|dictsort` which sorts the map by -\n key before iterating. -\n ```jinja -\n

-\n {% for key, value in my_dict|items %} -\n
{{ key }} -\n
{{ value }} -\n {% endfor %} -\n
-\n ```" - ' > items.md - - -echo 'reverse\nReverses a list or string -\n ```jinja -\n {% for user in users|reverse %} -\n
  • {{ user.name }} -\n {% endfor %} -\n ``` ' > reverse.md - -echo 'trim\n Trims a value -' >trim.md - -echo 'join\n Joins a sequence by a character -' >join.md - -echo 'abs\n \n Returns the absolute value of a number. -\n ```jinja -\n |a - b| = {{ (a - b)|abs }} -\n -> |2 - 4| = 2 -\n ``` -' > abs.md - - - -echo 'int\n \n Converts a value into an integer. -\n ```jinja -\n {{ "42"|int == 42 }} -> true -\n ``` -' > int.md - - - -echo 'float\n \n Converts a value into a float. -\n ```jinja -\n {{ "42.5"|float == 42.5 }} -> true -\n ``` -' > float.md - -echo 'attr\n Looks up an attribute. -\n In MiniJinja this is the same as the `[]` operator. In Jinja2 there is a -\n small difference which is why this filter is sometimes used in Jinja2 -\n templates. For compatibility it s provided here as well. -\n ```jinja -\n {{ value["key"] == value|attr("key") }} -> true -\n ``` -' > attr.md - -echo 'round\n \n Round the number to a given precision. -\n Round the number to a given precision. The first parameter specifies the -\n precision (default is 0). -\n ```jinja -\n {{ 42.55|round }} -\n -> 43.0 -\n ``` - -' > round.md - -echo 'first \n Returns the first item from a list. -\n If the list is empty `undefined` is returned. -\n ```jinja -\n
    -\n
    primary email -\n
    {{ user.email_addresses|first|default('no userecho 'first\n) }} -\n
    -\n ``` -' > first.md - -echo 'last\nReturns the last item from a list. -\n If the list is empty `undefined` is returned. -\n ```jinja -\n

    Most Recent Update

    -\n {% with update = updates|last %} -\n
    -\n
    Location -\n
    {{ update.location }} -\n
    Status -\n
    {{ update.status }} -\n
    -\n {% endwith %} -\n ``` - -' > last.md - -echo 'min\n \n Returns the smallest item from the list. -' > min.md - -echo 'max\n \n Returns the largest item from the list. -' > max.md - -echo 'sort\n \n Returns the sorted version of the given list. -\n The filter accepts a few keyword arguments: -\n * `case_sensitive`: set to `true` to make the sorting of strings case sensitive. -\n * `attribute`: can be set to an attribute or dotted path to sort by that attribute -\n * `reverse`: set to `true` to sort in reverse. -\n ```jinja -\n {{ [1, 3, 2, 4]|sort }} -> [4, 3, 2, 1] -\n {{ [1, 3, 2, 4]|sort(reverse=true) }} -> [1, 2, 3, 4] -\n # Sort users by age attribute in descending order. -\n {{ users|sort(attribute="age") }} -\n # Sort users by age attribute in ascending order. -\n {{ users|sort(attribute="age", reverse=true) }} -\n ``` - -' > sort.md - -echo 'list\n Converts the input value into a list. -\n If the value is already a list, then it s returned unchanged. -\n Applied to a map this returns the list of keys, applied to a -\n string this returns the characters. If the value is undefined -\n an empty list is returned. -' > list.md - -echo 'bool\n Converts the value into a boolean value. -\n This behaves the same as the if statement does with regards to -\n handling of boolean values. -' > bool.md - -echo 'slice\n Slice an iterable and return a list of lists containing -\n those items. -\n Useful if you want to create a div containing three ul tags that -\n represent columns: -\n ```jinja -\n
    -\n {% for column in items|slice(3) %} -\n
      -\n {% for item in column %} -\n
    • {{ item }}
    • -\n {% endfor %} -\n
    -\n {% endfor %} -\n
    -\n ``` -\n If you pass it a second argument it’s used to fill missing values on the -\n last iteration. -' > slice.md - -echo 'urlencode -\n URL encodes a value. -\n If given a map it encodes the parameters into a query set, otherwise it -\n encodes the stringified value. If the value is none or undefined, an -\n empty string is returned. -\n ```jinja -\n Search -\n ``` -' > urlencode.md - -echo 'selectattr\nCreates a new sequence of values of which an attribute passes a test. -\n This functions like [`select`] but it will test an attribute of the -\n object itself: -\n ```jinja -\n {{ users|selectattr("is_active") }} -> all users where x.is_active is true -\n {{ users|selectattr("id", "even") }} -> returns all users with an even id -\n ``` -' > selectattr.md - -echo 'reject\n Creates a new sequence of values that don t pass a test. -\n This is the inverse of [`select`]. -' > reject.md - -echo 'rejectattr -\n Creates a new sequence of values of which an attribute does not pass a test. -\n This functions like [`select`] but it will test an attribute of the -\n object itself: -\n ```jinja -\n {{ users|rejectattr("is_active") }} -> all users where x.is_active is false -\n {{ users|rejectattr("id", "even") }} -> returns all users with an odd id -\n ``` -' > rejectattr.md -echo 'map -\n Applies a filter to a sequence of objects or looks up an attribute. -\n This is useful when dealing with lists of objects but you are really -\n only interested in a certain value of it. -\n The basic usage is mapping on an attribute. Given a list of users -\n you can for instance quickly select the username and join on it: -\n ```jinja -\n {{ users|map(attribute="username")|join(', ') }} -\n ``` -\n You can specify a `default` value to use if an object in the list does -\n not have the given attribute. -\n ```jinja -\n {{ users|map(attribute="username", default="Anonymous")|join(", ") }} -\n ``` -\n Alternatively you can have `map` invoke a filter by passing the name of the -\n filter and the arguments afterwards. A good example would be applying a -\n text conversion filter on a sequence: -\n ```jinja -\n Users on this page: {{ titles|map("lower")|join(', ') }} -\n ``` -' > map.md - -echo 'unique\n Returns a list of unique items from the given iterable. -\n ```jinja -\n {{ ["foo", "bar", "foobar", "foobar"]|unique|list }} -\n -> ["foo", "bar", "foobar"] -\n ``` -\n The unique items are yielded in the same order as their first occurrence -\n in the iterable passed to the filter. The filter will not detect -\n duplicate objects or arrays, only primitives such as strings or numbers. -' > unique.md - -echo 'pprint - \n Pretty print a variable. - \n This is useful for debugging as it better shows what s inside an object. -' > pprint.md diff --git a/jinja-lsp/src/filter/md/filters/some4.sh b/jinja-lsp/src/filter/md/filters/some4.sh deleted file mode 100644 index f5327a5..0000000 --- a/jinja-lsp/src/filter/md/filters/some4.sh +++ /dev/null @@ -1,74 +0,0 @@ - -echo 'batch -\n Batch items. -\n This filter works pretty much like `slice` just the other way round. It -\n returns a list of lists with the given number of items. If you provide a -\n second parameter this is used to fill up missing items. -\n ```jinja -\n -\n {% for row in items|batch(3, " ") %} -\n -\n {% for column in row %} -\n -\n {% endfor %} -\n -\n {% endfor %} -\n
    {{ column }}
    -\n ``` -' > batch.md - - - -echo 'indent -\n indents Value with spaces -\n The first optional parameter to the filter can be set to `true` to -\n indent the first line. The parameter defaults to false. -\n the second optional parameter to the filter can be set to `true` -\n to indent blank lines. The parameter defaults to false. -\n This filter is useful, if you want to template yaml-files -\n ```jinja -\n example: -\n config: -\n {{ global_conifg|indent(2) }} # does not indent first line -\n {{ global_config|indent(2,true) }} # indent whole Value with two spaces -\n {{ global_config|indent(2,true,true)}} # indent whole Value and all blank lines -\n ``` ' > indent.md - - - -echo 'select\n -\n Creates a new sequence of values that pass a test. -\n Filters a sequence of objects by applying a test to each object. -\n Only values that pass the test are included. -\n If no test is specified, each object will be evaluated as a boolean. -\n ```jinja -\n {{ [1, 2, 3, 4]|select("odd") }} -> [1, 3] -\n {{ [false, null, 42]|select }} -> [42] -\n ``` -' > select.md - - -echo 'tojson\n -\n Dumps a value to JSON. -\n This filter is only available if the `json` feature is enabled. The resulting -\n value is safe to use in HTML as well as it will not contain any special HTML -\n characters. The optional parameter to the filter can be set to `true` to enable -\n pretty printing. Not that the `"` character is left unchanged as it s the -\n JSON string delimiter. If you want to pass JSON serialized this way into an -\n HTTP attribute use single quoted HTML attributes: -\n ```jinja -\n -\n ... -\n ``` -' > tojson.md - - - -echo 'default\n\n If the value is undefined it will return the passed default value, -\n otherwise the value of the variable: -\n ```jinja -|a - b| = {{ (a - b)|abs }} - -> |2 - 4| = 2 -\n ```' > default.md diff --git a/jinja-lsp/src/lsp_files.rs b/jinja-lsp/src/lsp_files.rs index efc23e1..ada97d8 100644 --- a/jinja-lsp/src/lsp_files.rs +++ b/jinja-lsp/src/lsp_files.rs @@ -1,6 +1,8 @@ use jinja_lsp_queries::tree_builder::{DataType, JinjaDiagnostic, JinjaVariable, LangType}; use std::{collections::HashMap, fs::read_to_string, path::Path}; -use tower_lsp::lsp_types::{CompletionItemKind, DidOpenTextDocumentParams}; +use tower_lsp::lsp_types::{ + CompletionItemKind, CompletionTextEdit, DidOpenTextDocumentParams, TextEdit, +}; use jinja_lsp_queries::{ capturer::{ @@ -273,7 +275,19 @@ impl LspFiles { false, capturer, ); - // TODO check for include capturer + if let Some(completion) = props.completion(point) { + return Some(completion); + } + let query = &self.queries.jinja_imports; + let capturer = IncludeCapturer::default(); + let props = query_props( + closest_node, + &writter.content, + point, + query, + false, + capturer, + ); props.completion(point) } @@ -490,6 +504,40 @@ impl LspFiles { None } + pub fn read_templates( + &self, + mut prefix: String, + config: &JinjaConfig, + range: Range, + ) -> Option> { + let all_templates = self.trees.get(&LangType::Template)?; + if prefix.is_empty() { + prefix = String::from("file:///"); + } + let templates = all_templates + .keys() + .filter(|template| template.contains(&prefix)); + let mut abc = vec![]; + for template in templates { + let c = &config.templates.replace('.', ""); + let mut parts = template.split(c); + parts.next(); + let label = parts.next()?.replacen('/', "", 1); + let new_text = format!("\"{label}\""); + let text_edit = CompletionTextEdit::Edit(TextEdit::new(range, new_text)); + let item = CompletionItem { + label, + detail: Some("Jinja template".to_string()), + kind: Some(CompletionItemKind::FILE), + text_edit: Some(text_edit), + ..Default::default() + }; + abc.push(item); + } + + Some(abc) + } + pub fn did_open( &mut self, params: DidOpenTextDocumentParams,