mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-26 05:16:29 +08:00
check if section has valid parent when importing notebook (#984)
* check if section has valid parent + tests * formatting * apply review feedback * fix the forgotten test * apply feedback * apply feedback * produce a warning if the section points to itself + tests * Apply suggestions from code review Co-authored-by: Jonatan Kłosko <jonatanklosko@gmail.com>
This commit is contained in:
parent
9c871960f3
commit
19f4f17e74
2 changed files with 151 additions and 7 deletions
|
|
@ -16,9 +16,9 @@ defmodule Livebook.LiveMarkdown.Import do
|
||||||
{ast, rewrite_messages} = rewrite_ast(ast)
|
{ast, rewrite_messages} = rewrite_ast(ast)
|
||||||
elements = group_elements(ast)
|
elements = group_elements(ast)
|
||||||
{notebook, build_messages} = build_notebook(elements)
|
{notebook, build_messages} = build_notebook(elements)
|
||||||
notebook = postprocess_notebook(notebook)
|
{notebook, postprocess_messages} = postprocess_notebook(notebook)
|
||||||
|
|
||||||
{notebook, earmark_messages ++ rewrite_messages ++ build_messages}
|
{notebook, earmark_messages ++ rewrite_messages ++ build_messages ++ postprocess_messages}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp earmark_message_to_string({_severity, line_number, message}) do
|
defp earmark_message_to_string({_severity, line_number, message}) do
|
||||||
|
|
@ -393,19 +393,47 @@ defmodule Livebook.LiveMarkdown.Import do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp postprocess_notebook(notebook) do
|
defp postprocess_notebook(notebook) do
|
||||||
sections =
|
{sections, {_branching_ids, warnings}} =
|
||||||
Enum.map(notebook.sections, fn section ->
|
notebook.sections
|
||||||
|
|> Enum.with_index()
|
||||||
|
|> Enum.map_reduce({MapSet.new(), []}, fn {section, section_idx},
|
||||||
|
{branching_ids, warnings} ->
|
||||||
# Set parent_id based on the persisted branch_parent_index if present
|
# Set parent_id based on the persisted branch_parent_index if present
|
||||||
case section.parent_id do
|
case section.parent_id do
|
||||||
nil ->
|
nil ->
|
||||||
section
|
{section, {branching_ids, warnings}}
|
||||||
|
|
||||||
{:idx, parent_idx} ->
|
{:idx, parent_idx} ->
|
||||||
parent = Enum.at(notebook.sections, parent_idx)
|
parent = Enum.at(notebook.sections, parent_idx)
|
||||||
%{section | parent_id: parent.id}
|
|
||||||
|
cond do
|
||||||
|
section_idx <= parent_idx ->
|
||||||
|
{%{section | parent_id: nil},
|
||||||
|
{
|
||||||
|
branching_ids,
|
||||||
|
[
|
||||||
|
~s{ignoring the parent section of "#{section.name}", because it comes later in the notebook}
|
||||||
|
| warnings
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
|
||||||
|
!is_nil(parent) && MapSet.member?(branching_ids, parent.id) ->
|
||||||
|
{%{section | parent_id: nil},
|
||||||
|
{
|
||||||
|
branching_ids,
|
||||||
|
[
|
||||||
|
~s{ignoring the parent section of "#{section.name}", because it is itself a branching section}
|
||||||
|
| warnings
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
|
||||||
|
true ->
|
||||||
|
{%{section | parent_id: parent.id},
|
||||||
|
{MapSet.put(branching_ids, section.id), warnings}}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
%{notebook | sections: sections}
|
{%{notebook | sections: sections}, Enum.reverse(warnings)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -766,4 +766,120 @@ defmodule Livebook.LiveMarkdown.ImportTest do
|
||||||
} = notebook
|
} = notebook
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "import notebook with invalid parent section produces a warning" do
|
||||||
|
markdown = """
|
||||||
|
# My Notebook
|
||||||
|
|
||||||
|
<!-- livebook:{"branch_parent_index":4} -->
|
||||||
|
|
||||||
|
## Section 1
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Process.info()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Section 2
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Process.info()
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert {notebook,
|
||||||
|
[
|
||||||
|
"ignoring the parent section of \"Section 1\", because it comes later in the notebook"
|
||||||
|
]} = Import.notebook_from_markdown(markdown)
|
||||||
|
|
||||||
|
assert %Notebook{
|
||||||
|
name: "My Notebook",
|
||||||
|
sections: [
|
||||||
|
%Notebook.Section{
|
||||||
|
parent_id: nil
|
||||||
|
},
|
||||||
|
%Notebook.Section{
|
||||||
|
parent_id: nil
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} = notebook
|
||||||
|
end
|
||||||
|
|
||||||
|
test "import notebook with parent section pointing to the section itself produces a warning" do
|
||||||
|
markdown = """
|
||||||
|
# My Notebook
|
||||||
|
|
||||||
|
<!-- livebook:{"branch_parent_index":0} -->
|
||||||
|
|
||||||
|
## Section 1
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Process.info()
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert {notebook,
|
||||||
|
[
|
||||||
|
"ignoring the parent section of \"Section 1\", because it comes later in the notebook"
|
||||||
|
]} = Import.notebook_from_markdown(markdown)
|
||||||
|
|
||||||
|
assert %Notebook{
|
||||||
|
name: "My Notebook",
|
||||||
|
sections: [
|
||||||
|
%Notebook.Section{
|
||||||
|
parent_id: nil
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} = notebook
|
||||||
|
end
|
||||||
|
|
||||||
|
test "import notebook with parent section being a branching section itself produces a warning" do
|
||||||
|
markdown = """
|
||||||
|
# My Notebook
|
||||||
|
|
||||||
|
|
||||||
|
## Section 1
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Process.info()
|
||||||
|
```
|
||||||
|
|
||||||
|
<!-- livebook:{"branch_parent_index":0} -->
|
||||||
|
|
||||||
|
## Section 2
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Process.info()
|
||||||
|
```
|
||||||
|
<!-- livebook:{"branch_parent_index":1} -->
|
||||||
|
|
||||||
|
## Section 3
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
Process.info()
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert {notebook,
|
||||||
|
[
|
||||||
|
"ignoring the parent section of \"Section 3\", because it is itself a branching section"
|
||||||
|
]} = Import.notebook_from_markdown(markdown)
|
||||||
|
|
||||||
|
assert %Notebook{
|
||||||
|
name: "My Notebook",
|
||||||
|
sections: [
|
||||||
|
%Notebook.Section{
|
||||||
|
name: "Section 1",
|
||||||
|
parent_id: nil
|
||||||
|
},
|
||||||
|
%Notebook.Section{
|
||||||
|
name: "Section 2",
|
||||||
|
parent_id: _
|
||||||
|
},
|
||||||
|
%Notebook.Section{
|
||||||
|
name: "Section 3",
|
||||||
|
parent_id: nil
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} = notebook
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue