flake_edit/
walk.rs

1use std::collections::HashMap;
2
3use rnix::{Root, SyntaxKind, SyntaxNode};
4
5use crate::{
6    change::Change,
7    edit::{OutputChange, Outputs},
8    input::Input,
9};
10
11#[derive(Debug, Clone)]
12pub struct Walker {
13    pub root: SyntaxNode,
14    pub inputs: HashMap<String, Input>,
15    pub add_toplevel: bool,
16}
17
18#[derive(Debug, Clone)]
19/// A helper for the [`Walker`], in order to hold context while traversing the tree.
20pub struct Context {
21    level: Vec<String>,
22}
23
24impl Context {
25    fn new(level: Vec<String>) -> Self {
26        Self { level }
27    }
28
29    pub fn level(&self) -> Vec<String> {
30        self.level.clone()
31    }
32}
33
34impl<'a> Walker {
35    pub fn new(stream: &'a str) -> Self {
36        let root = Root::parse(stream).syntax();
37        Self {
38            root,
39            inputs: HashMap::new(),
40            add_toplevel: false,
41        }
42    }
43    /// Traverse the toplevel `flake.nix` file.
44    /// It should consist of three attribute keys:
45    /// - description
46    /// - inputs
47    /// - outputs
48    pub fn walk(&mut self, change: &Change) -> Option<SyntaxNode> {
49        let cst = &self.root;
50        if cst.kind() != SyntaxKind::NODE_ROOT {
51            // TODO: handle this as an error
52            panic!("Should be a topevel node.")
53        } else {
54            self.walk_toplevel(cst.clone(), None, change)
55        }
56    }
57    /// Insert a new Input node at the correct position
58    /// or update it with new information.
59    fn insert_with_ctx(&mut self, id: String, input: Input, ctx: &Option<Context>) {
60        tracing::debug!("Inserting id: {id}, input: {input:?} with: ctx: {ctx:?}");
61
62        if let Some(ctx) = ctx {
63            // TODO: add more nesting
64            if let Some(follows) = ctx.level.first() {
65                if let Some(node) = self.inputs.get_mut(follows) {
66                    // TODO: only indirect follows is handled
67                    node.follows
68                        .push(crate::input::Follows::Indirect(id, input.url));
69                    // TODO: this should not be necessary
70                    node.follows.sort();
71                    node.follows.dedup();
72                } else {
73                    // In case the Input is not fully constructed
74                    let mut stub = Input::new(follows.to_string());
75                    stub.follows
76                        .push(crate::input::Follows::Indirect(id, input.url));
77                    self.inputs.insert(follows.to_string(), stub);
78                }
79            }
80        } else {
81            // Update the input, in case there was already a stub present.
82            if let Some(node) = self.inputs.get_mut(&id) {
83                if !input.url.to_string().is_empty() {
84                    node.url = input.url;
85                }
86                if !input.flake {
87                    node.flake = input.flake;
88                }
89            } else {
90                self.inputs.insert(id, input);
91            }
92        }
93        tracing::debug!("Self Inputs: {:#?}", self.inputs);
94    }
95
96    fn remove_child_with_whitespace(
97        parent: &SyntaxNode,
98        node: &SyntaxNode,
99        index: usize,
100    ) -> SyntaxNode {
101        let mut green = parent.green().remove_child(index);
102        if let Some(prev) = node.prev_sibling_or_token() {
103            if let SyntaxKind::TOKEN_WHITESPACE = prev.kind() {
104                green = green.remove_child(prev.index());
105            }
106        } else if let Some(next) = node.next_sibling_or_token() {
107            if let SyntaxKind::TOKEN_WHITESPACE = next.kind() {
108                green = green.remove_child(next.index());
109            }
110        }
111        Root::parse(green.to_string().as_str()).syntax()
112    }
113
114    /// Only walk the outputs attribute
115    pub(crate) fn list_outputs(&mut self) -> Outputs {
116        let mut outputs: Vec<String> = vec![];
117        let mut any = false;
118        tracing::debug!("Walking outputs.");
119        let cst = &self.root;
120        if cst.kind() != SyntaxKind::NODE_ROOT {
121            // TODO: handle this as an error
122            panic!("Should be a topevel node.")
123        }
124
125        for toplevel in cst.first_child().unwrap().children() {
126            if toplevel.kind() == SyntaxKind::NODE_ATTRPATH_VALUE {
127                {
128                    if let Some(outputs_node) = toplevel
129                        .children()
130                        .find(|child| child.to_string() == "outputs")
131                    {
132                        assert!(outputs_node.kind() == SyntaxKind::NODE_ATTRPATH);
133
134                        if let Some(outputs_lambda) = outputs_node.next_sibling() {
135                            assert!(outputs_lambda.kind() == SyntaxKind::NODE_LAMBDA);
136                            if let Some(output) = outputs_lambda
137                                .children()
138                                .find(|n| n.kind() == SyntaxKind::NODE_PATTERN)
139                            {
140                                // We need to iterate over tokens, because ellipsis ...
141                                // is not a valid node itself.
142                                for child in output.children_with_tokens() {
143                                    if child.kind() == SyntaxKind::NODE_PAT_ENTRY {
144                                        outputs.push(child.to_string());
145                                    }
146                                    if child.kind() == SyntaxKind::TOKEN_ELLIPSIS {
147                                        any = true;
148                                    }
149                                }
150                            }
151                        }
152                    }
153                }
154            }
155        }
156        if outputs.is_empty() {
157            Outputs::None
158        } else if any {
159            Outputs::Any(outputs)
160        } else {
161            Outputs::Multiple(outputs)
162        }
163    }
164    /// Only change the outputs attribute
165    pub(crate) fn change_outputs(&mut self, change: OutputChange) -> Option<SyntaxNode> {
166        tracing::debug!("Changing outputs.");
167        let cst = &self.root;
168        if cst.kind() != SyntaxKind::NODE_ROOT {
169            // TODO: handle this as an error
170            panic!("Should be a toplevel node.")
171        }
172
173        for toplevel in cst.first_child().unwrap().children() {
174            if toplevel.kind() == SyntaxKind::NODE_ATTRPATH_VALUE {
175                {
176                    if let Some(outputs_node) = toplevel
177                        .children()
178                        .find(|child| child.to_string() == "outputs")
179                    {
180                        assert!(outputs_node.kind() == SyntaxKind::NODE_ATTRPATH);
181
182                        if let Some(outputs_lambda) = outputs_node.next_sibling() {
183                            assert!(outputs_lambda.kind() == SyntaxKind::NODE_LAMBDA);
184                            for output in outputs_lambda.children() {
185                                if SyntaxKind::NODE_PATTERN == output.kind() {
186                                    if let OutputChange::Add(ref add) = change {
187                                        let token_count = output.children_with_tokens().count();
188                                        let count = output.children().count();
189                                        let last_node = token_count - 2;
190
191                                        // Adjust the addition for trailing slasheks
192                                        let addition = if let Some(SyntaxKind::TOKEN_COMMA) = output
193                                            .children()
194                                            .last()
195                                            .and_then(|last| last.next_sibling_or_token())
196                                            .map(|last_token| last_token.kind())
197                                        {
198                                            Root::parse(&format!("{add},")).syntax()
199                                        } else {
200                                            Root::parse(&format!(", {add}")).syntax()
201                                        };
202
203                                        let mut green = output
204                                            .green()
205                                            .insert_child(last_node, addition.green().into());
206                                        if let Some(prev) = output
207                                            .children()
208                                            .nth(count - 1)
209                                            .unwrap()
210                                            .prev_sibling_or_token()
211                                        {
212                                            if let SyntaxKind::TOKEN_WHITESPACE = prev.kind() {
213                                                let whitespace = Root::parse(
214                                                    prev.as_token().unwrap().green().text(),
215                                                )
216                                                .syntax();
217                                                green = green.insert_child(
218                                                    last_node,
219                                                    whitespace.green().into(),
220                                                );
221                                            }
222                                        }
223                                        let changed_outputs_lambda = outputs_lambda
224                                            .green()
225                                            .replace_child(output.index(), green.into());
226                                        let changed_toplevel = toplevel.green().replace_child(
227                                            outputs_lambda.index(),
228                                            changed_outputs_lambda.into(),
229                                        );
230                                        let result =
231                                            cst.first_child().unwrap().green().replace_child(
232                                                toplevel.index(),
233                                                changed_toplevel.into(),
234                                            );
235                                        return Some(Root::parse(&result.to_string()).syntax());
236                                    }
237
238                                    for child in output.children() {
239                                        if child.kind() == SyntaxKind::NODE_PAT_ENTRY {
240                                            if let OutputChange::Remove(ref id) = change {
241                                                if child.to_string() == *id {
242                                                    let mut green =
243                                                        output.green().remove_child(child.index());
244                                                    if let Some(prev) =
245                                                        child.prev_sibling_or_token()
246                                                    {
247                                                        if let SyntaxKind::TOKEN_WHITESPACE =
248                                                            prev.kind()
249                                                        {
250                                                            green =
251                                                                green.remove_child(prev.index());
252                                                            green = green
253                                                                .remove_child(prev.index() - 1);
254                                                        }
255                                                    } else if let Some(next) =
256                                                        child.next_sibling_or_token()
257                                                    {
258                                                        if let SyntaxKind::TOKEN_WHITESPACE =
259                                                            next.kind()
260                                                        {
261                                                            green =
262                                                                green.remove_child(next.index());
263                                                        }
264                                                    }
265                                                    let changed_outputs_lambda =
266                                                        outputs_lambda.green().replace_child(
267                                                            output.index(),
268                                                            green.into(),
269                                                        );
270                                                    let changed_toplevel =
271                                                        toplevel.green().replace_child(
272                                                            outputs_lambda.index(),
273                                                            changed_outputs_lambda.into(),
274                                                        );
275                                                    let result = cst
276                                                        .first_child()
277                                                        .unwrap()
278                                                        .green()
279                                                        .replace_child(
280                                                            toplevel.index(),
281                                                            changed_toplevel.into(),
282                                                        );
283                                                    return Some(
284                                                        Root::parse(&result.to_string()).syntax(),
285                                                    );
286                                                }
287                                            }
288                                        }
289                                    }
290                                }
291                            }
292                        }
293                    }
294                }
295            }
296        }
297        None
298    }
299    /// Traverse the toplevel `flake.nix` file.
300    /// It should consist of three attribute keys:
301    /// - description
302    /// - inputs
303    /// - outputs
304    #[allow(clippy::option_map_unit_fn)]
305    fn walk_toplevel(
306        &mut self,
307        node: SyntaxNode,
308        ctx: Option<Context>,
309        change: &Change,
310    ) -> Option<SyntaxNode> {
311        for root in node.children() {
312            // Because it is the node root this is the toplevel attribute
313            for toplevel in root.children() {
314                // Match attr_sets inputs, and outputs
315                tracing::debug!("Toplevel: {}", toplevel);
316                tracing::debug!("Kind: {:?}", toplevel.kind());
317                if toplevel.kind() == SyntaxKind::NODE_ATTRPATH_VALUE {
318                    for child in toplevel.children() {
319                        tracing::debug!("Toplevel Child: {child}");
320                        tracing::debug!("Toplevel Child Kind: {:?}", child.kind());
321                        tracing::debug!("Toplevel Child Index: {:?}", child.index());
322                        tracing::debug!("Toplevel Index: {:?}", toplevel.index());
323                        if let Some(parent) = child.parent() {
324                            tracing::debug!("Toplevel Child Parent: {}", parent);
325                            tracing::debug!("Toplevel Child Parent Kind: {:?}", parent.kind());
326                            tracing::debug!("Toplevel Child Parent Index: {:?}", parent.index());
327                        }
328                        if child.to_string() == "description" {
329                            // We are not interested in the description
330                            break;
331                        }
332                        if child.to_string() == "inputs" {
333                            if let Some(replacement) =
334                                self.walk_inputs(child.next_sibling().unwrap(), &ctx, change)
335                            {
336                                tracing::debug!("Replacement Node: {replacement}");
337                                let green = toplevel.green().replace_child(
338                                    child.next_sibling().unwrap().index(),
339                                    replacement.green().into(),
340                                );
341                                let green = toplevel.replace_with(green);
342                                let node = Root::parse(green.to_string().as_str()).syntax();
343                                return Some(node);
344                            }
345                        } else if child.to_string().starts_with("inputs") {
346                            // This is a toplevel node, of the form:
347                            // input.id ...
348                            // If the node should be empty,
349                            // it's toplevel should be empty too.
350                            if let Some(replacement) = self.walk_inputs(child.clone(), &ctx, change)
351                            {
352                                if replacement.to_string().is_empty() {
353                                    let node = Self::remove_child_with_whitespace(
354                                        &root,
355                                        &toplevel,
356                                        toplevel.index(),
357                                    );
358                                    return Some(node);
359                                } else {
360                                    tracing::debug!("Replacement Node: {replacement}");
361                                    let green = toplevel.green().replace_child(
362                                        child.next_sibling().unwrap().index(),
363                                        replacement.green().into(),
364                                    );
365                                    let green = toplevel.replace_with(green);
366                                    let node = Root::parse(green.to_string().as_str()).syntax();
367                                    return Some(node);
368                                }
369                            }
370                        };
371                        // If we already see outputs, but have no inputs
372                        // we need to create a toplevel inputs attribute set.
373                        if child.to_string() == "outputs" && self.add_toplevel {
374                            if let Change::Add { id, uri, flake } = change {
375                                let addition = Root::parse(&format!(
376                                    "inputs.{}.url = \"{}\";",
377                                    id.clone().unwrap(),
378                                    uri.clone().unwrap()
379                                ))
380                                .syntax();
381                                // TODO Guard against indices that would be out of range here.
382                                if toplevel.index() > 0 {
383                                    let mut node = root.green().insert_child(
384                                        toplevel.index() - 1,
385                                        addition.green().into(),
386                                    );
387                                    root.children()
388                                        .find(|c| c.index() == toplevel.index() - 2)
389                                        .map(|c| {
390                                            if let Some(prev) = c.prev_sibling_or_token() {
391                                                if prev.kind() == SyntaxKind::TOKEN_WHITESPACE {
392                                                    let whitespace = Root::parse(
393                                                        prev.as_token().unwrap().green().text(),
394                                                    )
395                                                    .syntax();
396                                                    node = node.insert_child(
397                                                        toplevel.index() - 1,
398                                                        whitespace.green().into(),
399                                                    );
400                                                }
401                                            } else if let Some(next) = c.next_sibling_or_token() {
402                                                if next.kind() == SyntaxKind::TOKEN_WHITESPACE {
403                                                    let whitespace = Root::parse(
404                                                        next.as_token().unwrap().green().text(),
405                                                    )
406                                                    .syntax();
407                                                    node = node.insert_child(
408                                                        toplevel.index() - 1,
409                                                        whitespace.green().into(),
410                                                    );
411                                                }
412                                            }
413                                        });
414                                    if !flake {
415                                        let no_flake = Root::parse(&format!(
416                                            "inputs.{}.flake = false;",
417                                            id.clone().unwrap(),
418                                        ))
419                                        .syntax();
420                                        node = node.insert_child(
421                                            toplevel.index() + 1,
422                                            no_flake.green().into(),
423                                        );
424                                        root.children()
425                                            .find(|c| c.index() == toplevel.index() - 2)
426                                            .map(|c| {
427                                                if let Some(prev) = c.prev_sibling_or_token() {
428                                                    if prev.kind() == SyntaxKind::TOKEN_WHITESPACE {
429                                                        let whitespace = Root::parse(
430                                                            prev.as_token().unwrap().green().text(),
431                                                        )
432                                                        .syntax();
433                                                        node = node.insert_child(
434                                                            toplevel.index() + 1,
435                                                            whitespace.green().into(),
436                                                        );
437                                                    }
438                                                } else if let Some(next) = c.next_sibling_or_token()
439                                                {
440                                                    if next.kind() == SyntaxKind::TOKEN_WHITESPACE {
441                                                        let whitespace = Root::parse(
442                                                            next.as_token().unwrap().green().text(),
443                                                        )
444                                                        .syntax();
445                                                        node = node.insert_child(
446                                                            toplevel.index() + 1,
447                                                            whitespace.green().into(),
448                                                        );
449                                                    }
450                                                }
451                                            });
452                                    }
453                                    if let Some(prev) = child.next_sibling_or_token() {
454                                        if prev.kind() == SyntaxKind::TOKEN_WHITESPACE {
455                                            let whitespace = Root::parse(
456                                                prev.as_token().unwrap().green().text(),
457                                            )
458                                            .syntax();
459                                            node = node.insert_child(
460                                                child.index() + 1,
461                                                whitespace.green().into(),
462                                            );
463                                        }
464                                    }
465                                    return Some(Root::parse(&node.to_string()).syntax());
466                                }
467                            }
468                        }
469                    }
470                } else {
471                    // TODO: proper error handling.
472                    panic!("Should be a NODE_ATTRPATH_VALUE");
473                }
474            }
475        }
476        None
477    }
478    fn walk_inputs(
479        &mut self,
480        node: SyntaxNode,
481        ctx: &Option<Context>,
482        change: &Change,
483    ) -> Option<SyntaxNode> {
484        tracing::debug!("WalkInputs: \n{node}\n with ctx: {ctx:?}");
485        tracing::debug!("WalkInputsKind: {:?}", node.kind());
486        match node.kind() {
487            SyntaxKind::NODE_ATTR_SET => {}
488            SyntaxKind::NODE_ATTRPATH_VALUE => {}
489            SyntaxKind::NODE_IDENT => {}
490            SyntaxKind::NODE_ATTRPATH => {
491                let maybe_follows_id = node
492                    .children()
493                    .find(|child| child.to_string() == "follows")
494                    .and_then(|input_child| input_child.prev_sibling());
495                if let Some(follows_id) = &maybe_follows_id {
496                    let maybe_input_id = node
497                        .children()
498                        .find(|child| child.to_string() == "inputs")
499                        .and_then(|input_child| input_child.next_sibling());
500                    let ctx = maybe_input_id
501                        .clone()
502                        .map(|id| Context::new(vec![id.to_string()]));
503                    let mut input = Input::new(follows_id.to_string());
504                    input.url = node.next_sibling().unwrap().to_string();
505                    let text_range = node.next_sibling().unwrap().text_range();
506                    input.range = crate::input::Range::from_text_range(text_range);
507                    self.insert_with_ctx(follows_id.to_string(), input, &ctx);
508
509                    // Remove a toplevel follows node
510                    if let Some(input_id) = maybe_input_id {
511                        if change.is_remove() {
512                            if let Some(id) = change.id() {
513                                let maybe_follows = maybe_follows_id.map(|id| id.to_string());
514                                if id.matches_with_follows(&input_id.to_string(), maybe_follows) {
515                                    let replacement = Root::parse("").syntax();
516                                    return Some(replacement);
517                                }
518                            }
519                        }
520                    }
521                }
522            }
523            _ => {}
524        }
525        for child in node.children_with_tokens() {
526            tracing::debug!("Inputs Child Kind: {:?}", child.kind());
527            tracing::debug!("Inputs Child: {child}");
528            tracing::debug!("Inputs Child Len: {}", child.to_string().len());
529            match child.kind() {
530                SyntaxKind::NODE_ATTRPATH_VALUE => {
531                    // TODO: Append to context, instead of creating a new one.
532                    let ctx = if ctx.is_none() {
533                        let maybe_input_id = child.as_node().unwrap().children().find_map(|c| {
534                            c.children()
535                                .find(|child| child.to_string() == "inputs")
536                                .and_then(|input_child| input_child.prev_sibling())
537                        });
538                        maybe_input_id.map(|id| Context::new(vec![id.to_string()]))
539                    } else {
540                        ctx.clone()
541                    };
542                    if let Some(replacement) =
543                        self.walk_input(child.as_node().unwrap(), &ctx, change)
544                    {
545                        tracing::debug!("Child Id: {}", child.index());
546                        tracing::debug!("Input replacement node: {}", node);
547                        let mut green = node
548                            .green()
549                            .replace_child(child.index(), replacement.green().into());
550                        if replacement.text().is_empty() {
551                            let prev = child.prev_sibling_or_token();
552                            if let Some(prev) = prev {
553                                if let SyntaxKind::TOKEN_WHITESPACE = prev.kind() {
554                                    green = green.remove_child(prev.index());
555                                }
556                            } else if let Some(next) = child.next_sibling_or_token() {
557                                if let SyntaxKind::TOKEN_WHITESPACE = next.kind() {
558                                    green = green.remove_child(next.index());
559                                }
560                            }
561                        }
562                        let node = Root::parse(green.to_string().as_str()).syntax();
563                        return Some(node);
564                    } else if change.is_some() && change.id().is_some() && ctx.is_none() {
565                        if let Change::Add { id, uri, flake } = change {
566                            let uri = Root::parse(&format!(
567                                "{}.url = \"{}\";",
568                                id.clone().unwrap(),
569                                uri.clone().unwrap(),
570                            ))
571                            .syntax();
572                            let mut green =
573                                node.green().insert_child(child.index(), uri.green().into());
574                            let prev = child.prev_sibling_or_token().unwrap();
575                            tracing::debug!("Token:{}", prev);
576                            tracing::debug!("Token Kind: {:?}", prev.kind());
577                            if prev.kind() == SyntaxKind::TOKEN_WHITESPACE {
578                                let whitespace =
579                                    Root::parse(prev.as_token().unwrap().green().text()).syntax();
580                                green = green
581                                    .insert_child(child.index() + 1, whitespace.green().into());
582                            }
583                            tracing::debug!("green: {}", green);
584                            tracing::debug!("node: {}", node);
585                            tracing::debug!("node kind: {:?}", node.kind());
586                            if !flake {
587                                let no_flake = Root::parse(&format!(
588                                    "{}.flake = false;",
589                                    id.clone().unwrap(),
590                                ))
591                                .syntax();
592                                green =
593                                    green.insert_child(child.index() + 2, no_flake.green().into());
594                                if prev.kind() == SyntaxKind::TOKEN_WHITESPACE {
595                                    let whitespace =
596                                        Root::parse(prev.as_token().unwrap().green().text())
597                                            .syntax();
598                                    green = green
599                                        .insert_child(child.index() + 3, whitespace.green().into());
600                                }
601                            }
602                            let node = Root::parse(green.to_string().as_str()).syntax();
603                            return Some(node);
604                        }
605                    }
606                }
607                SyntaxKind::NODE_IDENT => {
608                    tracing::debug!("Node PATH: {}", child);
609                    if child.to_string() == "inputs" {
610                        if let Some(next_sibling) = child.as_node().unwrap().next_sibling() {
611                            match next_sibling.kind() {
612                                SyntaxKind::NODE_IDENT => {
613                                    tracing::debug!("NODE_IDENT input: {}", next_sibling);
614                                    tracing::debug!("NODE_IDENT input: {}", next_sibling);
615                                    if let Some(url_id) = next_sibling.next_sibling() {
616                                        match url_id.kind() {
617                                            SyntaxKind::NODE_IDENT => {
618                                                if url_id.to_string() == "url" {
619                                                    if let Some(url) = child
620                                                        .as_node()
621                                                        .unwrap()
622                                                        .parent()
623                                                        .unwrap()
624                                                        .next_sibling()
625                                                    {
626                                                        tracing::debug!(
627                                                            "This is an url from {} - {}",
628                                                            next_sibling,
629                                                            url
630                                                        );
631                                                        let mut input =
632                                                            Input::new(next_sibling.to_string());
633                                                        input.url = url.to_string();
634                                                        let text_range = url.text_range();
635                                                        input.range =
636                                                            crate::input::Range::from_text_range(
637                                                                text_range,
638                                                            );
639                                                        self.insert_with_ctx(
640                                                            next_sibling.to_string(),
641                                                            input,
642                                                            ctx,
643                                                        );
644                                                        if change.is_some() && change.is_remove() {
645                                                            if let Some(id) = change.id() {
646                                                                if id.to_string()
647                                                                    == next_sibling.to_string()
648                                                                {
649                                                                    let replacement =
650                                                                        Root::parse("").syntax();
651                                                                    tracing::debug!("Node: {node}");
652                                                                    return Some(replacement);
653                                                                }
654                                                            }
655                                                            if let Some(ctx) = ctx {
656                                                                if *ctx.level.first().unwrap()
657                                                                    == next_sibling.to_string()
658                                                                {
659                                                                    let replacement =
660                                                                        Root::parse("").syntax();
661                                                                    return Some(replacement);
662                                                                }
663                                                            }
664                                                        }
665                                                    }
666                                                } else if url_id.to_string() == "flake" {
667                                                    if let Some(is_flake) = child
668                                                        .as_node()
669                                                        .unwrap()
670                                                        .parent()
671                                                        .unwrap()
672                                                        .next_sibling()
673                                                    {
674                                                        tracing::debug!(
675                                                            "This id {} is a flake: {}",
676                                                            next_sibling,
677                                                            is_flake
678                                                        );
679                                                        // let mut input =
680                                                        //     Input::new(next_sibling.to_string());
681                                                        // input.flake =
682                                                        //     is_flake.to_string().parse().unwrap();
683                                                        // self.insert_with_ctx(
684                                                        //     next_sibling.to_string(),
685                                                        //     input,
686                                                        //     ctx,
687                                                        // );
688                                                        if change.is_some() && change.is_remove() {
689                                                            if let Some(id) = change.id() {
690                                                                if id.to_string()
691                                                                    == next_sibling.to_string()
692                                                                {
693                                                                    let replacement =
694                                                                        Root::parse("").syntax();
695                                                                    tracing::debug!("Node: {node}");
696                                                                    return Some(replacement);
697                                                                }
698                                                            }
699                                                            if let Some(ctx) = ctx {
700                                                                if *ctx.level.first().unwrap()
701                                                                    == next_sibling.to_string()
702                                                                {
703                                                                    let replacement =
704                                                                        Root::parse("").syntax();
705                                                                    return Some(replacement);
706                                                                }
707                                                            }
708                                                        }
709                                                    }
710                                                } else {
711                                                    tracing::debug!(
712                                                        "Unhandled input: {}",
713                                                        next_sibling
714                                                    );
715                                                }
716                                            }
717                                            _ => {
718                                                tracing::debug!(
719                                                    "Unhandled input: {}",
720                                                    next_sibling
721                                                );
722                                            }
723                                        }
724                                    } else {
725                                        tracing::debug!("Unhandled input: {}", next_sibling);
726                                        if let Some(nested_attr) = child
727                                            .as_node()
728                                            .unwrap()
729                                            .parent()
730                                            .unwrap()
731                                            .next_sibling()
732                                        {
733                                            tracing::debug!("Nested input: {}", nested_attr);
734                                            for attr in nested_attr.children() {
735                                                tracing::debug!(
736                                                    "Nested input attr: {}, from: {}",
737                                                    attr,
738                                                    next_sibling
739                                                );
740
741                                                for binding in attr.children() {
742                                                    if binding.to_string() == "url" {
743                                                        let url = binding.next_sibling().unwrap();
744                                                        tracing::debug!(
745                                                            "This is an url: {} - {}",
746                                                            next_sibling,
747                                                            url
748                                                        );
749                                                        let mut input =
750                                                            Input::new(next_sibling.to_string());
751                                                        input.url = url.to_string();
752                                                        let text_range = next_sibling.text_range();
753                                                        input.range =
754                                                            crate::input::Range::from_text_range(
755                                                                text_range,
756                                                            );
757                                                        self.insert_with_ctx(
758                                                            next_sibling.to_string(),
759                                                            input,
760                                                            ctx,
761                                                        );
762                                                    }
763                                                    if change.is_remove() {
764                                                        if let Some(id) = change.id() {
765                                                            if id.to_string()
766                                                                == next_sibling.to_string()
767                                                            {
768                                                                let replacement =
769                                                                    Root::parse("").syntax();
770                                                                tracing::debug!("Node: {node}");
771                                                                return Some(replacement);
772                                                            }
773                                                        }
774                                                    }
775                                                    tracing::debug!(
776                                                        "Nested input attr binding: {}",
777                                                        binding
778                                                    );
779                                                }
780                                                let context =
781                                                    Context::new(vec![next_sibling.to_string()]);
782                                                tracing::debug!(
783                                                    "Walking inputs with: {attr}, context: {context:?}"
784                                                );
785                                                if let Some(change) =
786                                                    self.walk_input(&attr, &Some(context), change)
787                                                {
788                                                    tracing::debug!("Adjusted change: {change}");
789                                                    tracing::debug!(
790                                                        "Adjusted change is_empty: {}",
791                                                        change.to_string().is_empty()
792                                                    );
793                                                    tracing::debug!(
794                                                        "Child index: {}",
795                                                        child.index()
796                                                    );
797                                                    tracing::debug!(
798                                                        "Child node: {}",
799                                                        child.as_node().unwrap()
800                                                    );
801                                                    tracing::debug!("Nested Attr: {}", nested_attr);
802                                                    tracing::debug!("Node: {}", node);
803                                                    tracing::debug!("Attr: {}", attr);
804                                                    // TODO: adjust node correctly if the change is
805                                                    // not empty
806                                                    let replacement =
807                                                        Self::remove_child_with_whitespace(
808                                                            &nested_attr,
809                                                            &attr,
810                                                            attr.index(),
811                                                        );
812                                                    tracing::debug!("Replacement: {}", replacement);
813                                                    return Some(replacement);
814                                                }
815                                            }
816                                        }
817                                    }
818                                }
819                                SyntaxKind::NODE_ATTR_SET => {}
820                                _ => {
821                                    tracing::debug!(
822                                        "Unhandled input kind: {:?}",
823                                        next_sibling.kind()
824                                    );
825                                    tracing::debug!("Unhandled input: {}", next_sibling);
826                                }
827                            }
828                        }
829                    }
830                    // TODO: flat tree attributes
831                    if child.to_string().starts_with("inputs") {
832                        let child_node = child.as_node().unwrap();
833                        let id = child_node.next_sibling().unwrap();
834                        let context = Context::new(vec![id.to_string()]);
835                        tracing::debug!("Walking inputs with: {child}, context: {context:?}");
836                        if let Some(_replacement) =
837                            self.walk_inputs(child_node.clone(), &Some(context), change)
838                        {
839                            panic!("Not yet implemented");
840                        }
841                    }
842                    if let Some(parent) = child.parent() {
843                        tracing::debug!("Children Parent Child: {}", child);
844                        tracing::debug!("Children Parent Child Kind: {:?}", child.kind());
845                        tracing::debug!("Children Parent Kind: {:?}", parent.kind());
846                        tracing::debug!("Children Parent: {}", parent);
847                        tracing::debug!("Children Parent Context: {:?}", ctx);
848                        if let Some(sibling) = parent.next_sibling() {
849                            tracing::debug!("Children Sibling: {}", sibling);
850                        }
851                        for child in parent.children() {
852                            tracing::debug!("Children Sibling --: {}", child);
853                        }
854                    }
855                }
856                _ => {
857                    tracing::debug!("UNMATCHED KIND: {:?}", child.kind());
858                    tracing::debug!("UNMATCHED PATH: {}", child);
859                }
860            }
861        }
862        None
863    }
864    /// Walk a single input field.
865    /// Example:
866    /// ```nix
867    ///  flake-utils.url = "github:numtide/flake-utils";
868    /// ```
869    /// or
870    /// ```nix
871    ///  rust-overlay = {
872    ///  url = "github:oxalica/rust-overlay";
873    ///  inputs.nixpkgs.follows = "nixpkgs";
874    ///  inputs.flake-utils.follows = "flake-utils";
875    ///  };
876    /// ```
877    fn walk_input(
878        &mut self,
879        node: &SyntaxNode,
880        ctx: &Option<Context>,
881        change: &Change,
882    ) -> Option<SyntaxNode> {
883        tracing::debug!("\nInput: {node}\n with ctx: {ctx:?}");
884        for (i, child) in node.children().enumerate() {
885            tracing::debug!("Kind #:{i} {:?}", child.kind());
886            tracing::debug!("Kind #:{i} {}", child);
887            if child.kind() == SyntaxKind::NODE_ATTRPATH {
888                for attr in child.children() {
889                    tracing::debug!("Child of ATTRPATH #:{i} {}", child);
890                    tracing::debug!("Child of ATTR #:{i} {}", attr);
891                    if attr.to_string() == "url" {
892                        if let Some(prev_id) = attr.prev_sibling() {
893                            if let Change::Remove { id } = change {
894                                if id.to_string() == prev_id.to_string() {
895                                    tracing::debug!("Removing: {id}");
896                                    let empty = Root::parse("").syntax();
897                                    return Some(empty);
898                                }
899                            }
900                            if let Some(sibling) = child.next_sibling() {
901                                tracing::debug!("This is an url from {} - {}", prev_id, sibling);
902                                let mut input = Input::new(prev_id.to_string());
903                                input.url = sibling.to_string();
904                                let text_range = sibling.text_range();
905                                input.range = crate::input::Range::from_text_range(text_range);
906                                self.insert_with_ctx(prev_id.to_string(), input, ctx);
907                            }
908                        }
909                        tracing::debug!("This is the parent: {}", child.parent().unwrap());
910                        tracing::debug!(
911                            "This is the next_sibling: {}",
912                            child.next_sibling().unwrap()
913                        );
914                        if let Some(parent) = child.parent() {
915                            if let Some(sibling) = parent.next_sibling() {
916                                //TODO: this is only matched, when url is the first child
917                                // TODO: Is this correct?
918                                tracing::debug!(
919                                    "This is a possible follows attribute:{} {}",
920                                    attr,
921                                    sibling
922                                );
923                                if let Some(child) = sibling.first_child() {
924                                    if child.to_string() == "inputs" {
925                                        if let Some(attr_set) = child.next_sibling() {
926                                            if SyntaxKind::NODE_ATTR_SET == attr_set.kind() {
927                                                for attr in attr_set.children() {
928                                                    let is_follows = attr
929                                                        .first_child()
930                                                        .unwrap()
931                                                        .first_child()
932                                                        .unwrap()
933                                                        .next_sibling()
934                                                        .unwrap();
935
936                                                    if is_follows.to_string() == "follows" {
937                                                        let id = is_follows.prev_sibling().unwrap();
938                                                        let follows = attr
939                                                            .first_child()
940                                                            .unwrap()
941                                                            .next_sibling()
942                                                            .unwrap();
943                                                        tracing::debug!(
944                                                            "The following attribute follows: {id}:{follows} is nested inside the attr: {ctx:?}"
945                                                        );
946                                                        let mut input = Input::new(id.to_string());
947                                                        input.url = follows.to_string();
948                                                        let text_range = follows.text_range();
949                                                        input.range =
950                                                            crate::input::Range::from_text_range(
951                                                                text_range,
952                                                            );
953                                                        self.insert_with_ctx(
954                                                            id.to_string(),
955                                                            input,
956                                                            ctx,
957                                                        );
958                                                        if change.is_remove() {
959                                                            if let Some(id) = change.id() {
960                                                                if id.matches_with_ctx(
961                                                                    &follows.to_string(),
962                                                                    ctx.clone(),
963                                                                ) {
964                                                                    let replacement =
965                                                                        Root::parse("").syntax();
966                                                                    return Some(replacement);
967                                                                }
968                                                            }
969                                                        }
970                                                    }
971                                                }
972                                            }
973                                        }
974                                    }
975                                }
976                            }
977                        }
978                    }
979                    if attr.to_string() == "flake" {
980                        if let Some(input_id) = attr.prev_sibling() {
981                            if let Some(is_flake) = attr.parent().unwrap().next_sibling() {
982                                tracing::debug!(
983                                    "The following attribute is a flake: {input_id}:{is_flake} is nested inside the context: {ctx:?}"
984                                );
985                                let mut input = Input::new(input_id.to_string());
986                                input.flake = is_flake.to_string().parse().unwrap();
987                                let text_range = input_id.text_range();
988                                input.range = crate::input::Range::from_text_range(text_range);
989                                self.insert_with_ctx(input_id.to_string(), input, ctx);
990                                if change.is_remove() {
991                                    if let Some(id) = change.id() {
992                                        if id.matches_with_ctx(&input_id.to_string(), ctx.clone()) {
993                                            let replacement = Root::parse("").syntax();
994                                            return Some(replacement);
995                                        }
996                                    }
997                                }
998                            }
999                        } else {
1000                            // TODO: handle this.
1001                            // This happens, when there is a nested node.
1002                            tracing::info!("Nested: This is not handled yet.");
1003                        }
1004                    }
1005                    if attr.to_string() == "follows" {
1006                        // Construct the follows attribute
1007                        // TODO:
1008                        // - check for possible removal / change
1009                        let id = attr.prev_sibling().unwrap();
1010                        let follows = attr.parent().unwrap().next_sibling().unwrap();
1011                        tracing::debug!(
1012                            "The following attribute follows: {id}:{follows} is nested inside the attr: {ctx:?}"
1013                        );
1014                        // TODO: Construct follows attribute if not yet ready.
1015                        // For now assume that the url is the first attribute.
1016                        // This assumption doesn't generally hold true.
1017                        let mut input = Input::new(id.to_string());
1018                        input.url = follows.to_string();
1019                        let text_range = follows.text_range();
1020                        input.range = crate::input::Range::from_text_range(text_range);
1021                        self.insert_with_ctx(id.to_string(), input.clone(), ctx);
1022                        if let Some(id) = change.id() {
1023                            if let Some(ctx) = ctx {
1024                                if id.matches_with_ctx(input.id(), Some(ctx.clone()))
1025                                    && change.is_remove()
1026                                {
1027                                    let replacement = Root::parse("").syntax();
1028                                    return Some(replacement);
1029                                }
1030                            }
1031                        }
1032                    }
1033                }
1034            }
1035            if child.kind() == SyntaxKind::NODE_ATTR_SET {
1036                for attr in child.children() {
1037                    tracing::debug!("Child of ATTRSET KIND #:{i} {:?}", attr.kind());
1038                    tracing::debug!("Child of ATTRSET #:{i} {}", attr);
1039                    for leaf in attr.children() {
1040                        tracing::debug!("LEAF of ATTRSET KIND #:{i} {:?}", leaf.kind());
1041                        tracing::debug!("LEAF of ATTRSET #:{i} {}", leaf);
1042                        if leaf.to_string() == "url" {
1043                            let id = child.prev_sibling().unwrap();
1044                            let uri = leaf.next_sibling().unwrap();
1045                            tracing::debug!("This is an url from {} - {}", id, uri,);
1046                            let mut input = Input::new(id.to_string());
1047                            input.url = uri.to_string();
1048                            let text_range = uri.text_range();
1049                            input.range = crate::input::Range::from_text_range(text_range);
1050                            self.insert_with_ctx(id.to_string(), input, ctx);
1051
1052                            // Remove matched node.
1053                            if let Change::Remove { id: candidate } = change {
1054                                if candidate.to_string() == id.to_string() {
1055                                    tracing::debug!("Removing: {id}");
1056                                    let empty = Root::parse("").syntax();
1057                                    return Some(empty);
1058                                }
1059                            }
1060                        }
1061                        if leaf.to_string().starts_with("inputs") {
1062                            let id = child.prev_sibling().unwrap();
1063                            let context = Context::new(vec![id.to_string()]);
1064                            tracing::debug!("Walking inputs with: {attr}, context: {context:?}");
1065                            // panic!("Walking inputs with: {attr}, context: {context:?}");
1066                            if let Some(replacement) =
1067                                self.walk_inputs(child.clone(), &Some(context), change)
1068                            {
1069                                // TODO: adjustment of whitespace, if node is empty
1070                                // TODO: if it leaves an empty attr, then remove whole?
1071                                let tree = node
1072                                    .green()
1073                                    .replace_child(child.index(), replacement.green().into());
1074                                let replacement = Root::parse(&tree.to_string()).syntax();
1075                                return Some(replacement);
1076                            }
1077                            tracing::debug!("Child of ATTRSET KIND #:{i} {:?}", leaf.kind());
1078                            tracing::debug!("Child of ATTRSET CHILD #:{i} {}", leaf);
1079                        }
1080                    }
1081                }
1082            }
1083            tracing::debug!("Child #:{i} {}", child);
1084        }
1085        None
1086    }
1087}