@Override
      protected void updateStructure(boolean fromRoot, @NotNull Set<? extends VirtualFile> updatedFiles) {
        boolean filtered = root.filter != null;
        SmartHashSet<Node> nodes = fromRoot || filtered ? null : new SmartHashSet<>();
        root.children.forEach(child -> child.invalidateChildren(node -> {
          if (!updatedFiles.contains(node.file)) return true;
          if (filtered) {
            node.resetVisibility(); // because of moving within the same root
            for (Node parent = node.parent; parent != null; parent = parent.parent) {
              parent.visibility = null;
            }
          }
          if (nodes == null) return false;
          Node n = node.file.isDirectory() ? node : node.parent;
          if (n == null) return false;
          for (Node p = n.parent; p != null; p = p.parent) {
            if (nodes.contains(p)) return false;
          }
          nodes.add(n);
          return false;
        }));
        if (nodes != null) {
          nodes.forEach(node -> {
            TreePath path = pathToCustomNode(node, child -> child.parent);
            if (path != null) pathChanged(path);
          });
        }
        else {
          root.valid = false; // need to reload content roots
          pathChanged(null);
        }
      }
@Override
      protected void updateStructure(boolean fromRoot, @NotNull Set<? extends VirtualFile> updatedFiles)