mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-11-09 21:51:42 +08:00
Add doctest decorations to monaco editor per result (#1911)
This commit is contained in:
parent
5c923b3a2c
commit
e614dcbac1
6 changed files with 157 additions and 1 deletions
|
|
@ -158,3 +158,37 @@ Also some spacing adjustments.
|
|||
margin-left: 2px;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* To style circles for doctest results */
|
||||
.line-circle-red {
|
||||
background-color: red;
|
||||
box-sizing: border-box;
|
||||
border-radius: 100%;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
max-width: 15px;
|
||||
height: 15px !important;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.line-circle-green {
|
||||
background-color: green;
|
||||
box-sizing: border-box;
|
||||
border-radius: 100%;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
max-width: 15px;
|
||||
height: 15px !important;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.line-circle-grey {
|
||||
background-color: grey;
|
||||
box-sizing: border-box;
|
||||
border-radius: 100%;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
max-width: 15px;
|
||||
height: 15px !important;
|
||||
margin: 3px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,6 +242,28 @@ const Cell = {
|
|||
liveEditor.setCodeErrorMarker(code_error);
|
||||
}
|
||||
);
|
||||
|
||||
this.handleEvent(`start_evaluation:${this.props.cellId}`, () => {
|
||||
liveEditor.clearDoctestDecorations();
|
||||
});
|
||||
|
||||
this.handleEvent(
|
||||
`doctest_result:${this.props.cellId}`,
|
||||
({ state, line }) => {
|
||||
console.log({ state, line });
|
||||
switch (state) {
|
||||
case "evaluating":
|
||||
liveEditor.addEvaluatingDoctestDecoration(line);
|
||||
break;
|
||||
case "success":
|
||||
liveEditor.addSuccessDoctestDecoration(line);
|
||||
break;
|
||||
case "failed":
|
||||
liveEditor.addFailedDoctestDecoration(line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -35,6 +35,15 @@ class LiveEditor {
|
|||
this._onBlur = [];
|
||||
this._onCursorSelectionChange = [];
|
||||
this._remoteUserByClientId = {};
|
||||
/* For doctest decorations we store the params to create the
|
||||
* decorations and also the result of creating the decorations.
|
||||
* The params are IModelDeltaDecoration from https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IModelDeltaDecoration.html
|
||||
* and the result is IEditorDecorationsCollection from https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IEditorDecorationsCollection.html
|
||||
*/
|
||||
this._doctestDecorations = {
|
||||
deltaDecorations: {},
|
||||
decorationCollection: null,
|
||||
};
|
||||
|
||||
const serverAdapter = new HookServerAdapter(hook, cellId, tag);
|
||||
this.editorClient = new EditorClient(serverAdapter, revision);
|
||||
|
|
@ -270,6 +279,9 @@ class LiveEditor {
|
|||
: "off",
|
||||
});
|
||||
|
||||
this._doctestDecorations.decorationCollection =
|
||||
this.editor.createDecorationsCollection([]);
|
||||
|
||||
this.editor.addAction({
|
||||
contextMenuGroupId: "word-wrapping",
|
||||
id: "enable-word-wrapping",
|
||||
|
|
@ -566,6 +578,40 @@ class LiveEditor {
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
clearDoctestDecorations() {
|
||||
this._doctestDecorations.decorationCollection.clear();
|
||||
this._doctestDecorations.deltaDecorations = {};
|
||||
}
|
||||
|
||||
_createDoctestDecoration(lineNumber, className) {
|
||||
return {
|
||||
range: new monaco.Range(lineNumber, 1, lineNumber, 1),
|
||||
options: {
|
||||
isWholeLine: true,
|
||||
linesDecorationsClassName: className,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
_addDoctestDecoration(line, className) {
|
||||
const newDecoration = this._createDoctestDecoration(line, className);
|
||||
this._doctestDecorations.deltaDecorations[line] = newDecoration;
|
||||
const decos = Object.values(this._doctestDecorations.deltaDecorations);
|
||||
this._doctestDecorations.decorationCollection.set(decos);
|
||||
}
|
||||
|
||||
addSuccessDoctestDecoration(line) {
|
||||
this._addDoctestDecoration(line, "line-circle-green");
|
||||
}
|
||||
|
||||
addFailedDoctestDecoration(line) {
|
||||
this._addDoctestDecoration(line, "line-circle-red");
|
||||
}
|
||||
|
||||
addEvaluatingDoctestDecoration(line) {
|
||||
this._addDoctestDecoration(line, "line-circle-grey");
|
||||
}
|
||||
}
|
||||
|
||||
function completionItemsToSuggestions(items, settings) {
|
||||
|
|
|
|||
|
|
@ -20,7 +20,12 @@ defmodule Livebook.Runtime.Evaluator.Doctests do
|
|||
tests =
|
||||
test_module.tests
|
||||
|> Enum.sort_by(& &1.tags.doctest_line)
|
||||
|> Enum.map(&run_test/1)
|
||||
|> Enum.map(fn test ->
|
||||
report_doctest_state(:evaluating, test)
|
||||
test = run_test(test)
|
||||
report_doctest_state(:success_or_failed, test)
|
||||
test
|
||||
end)
|
||||
|
||||
formatted = format_results(tests)
|
||||
put_output({:text, formatted})
|
||||
|
|
@ -35,6 +40,24 @@ defmodule Livebook.Runtime.Evaluator.Doctests do
|
|||
:ok
|
||||
end
|
||||
|
||||
defp report_doctest_state(:evaluating, test) do
|
||||
result = %{
|
||||
doctest_line: test.tags.doctest_line,
|
||||
state: :evaluating
|
||||
}
|
||||
|
||||
put_output({:doctest_result, result})
|
||||
end
|
||||
|
||||
defp report_doctest_state(:success_or_failed, test) do
|
||||
result = %{
|
||||
doctest_line: test.tags.doctest_line,
|
||||
state: get_in(test, [Access.key(:state), Access.elem(0)]) || :success
|
||||
}
|
||||
|
||||
put_output({:doctest_result, result})
|
||||
end
|
||||
|
||||
defp define_test_module(modules) do
|
||||
id =
|
||||
modules
|
||||
|
|
|
|||
|
|
@ -536,6 +536,19 @@ defmodule Livebook.Session.Data do
|
|||
end
|
||||
end
|
||||
|
||||
def apply_operation(
|
||||
data,
|
||||
{:add_cell_evaluation_output, _client_id, id, {:doctest_result, _result}}
|
||||
) do
|
||||
with {:ok, _cell, _} <- Notebook.fetch_cell_and_section(data.notebook, id) do
|
||||
data
|
||||
|> with_actions()
|
||||
|> wrap_ok()
|
||||
else
|
||||
_ -> :error
|
||||
end
|
||||
end
|
||||
|
||||
def apply_operation(data, {:add_cell_evaluation_output, _client_id, id, output}) do
|
||||
with {:ok, cell, _} <- Notebook.fetch_cell_and_section(data.notebook, id) do
|
||||
data
|
||||
|
|
|
|||
|
|
@ -1760,6 +1760,17 @@ defmodule LivebookWeb.SessionLive do
|
|||
end
|
||||
end
|
||||
|
||||
defp after_operation(
|
||||
socket,
|
||||
_prev_socket,
|
||||
{:add_cell_evaluation_output, _client_id, cell_id, {:doctest_result, result}}
|
||||
) do
|
||||
push_event(socket, "doctest_result:#{cell_id}", %{
|
||||
state: result.state,
|
||||
line: result.doctest_line
|
||||
})
|
||||
end
|
||||
|
||||
defp after_operation(
|
||||
socket,
|
||||
_prev_socket,
|
||||
|
|
@ -1821,6 +1832,10 @@ defmodule LivebookWeb.SessionLive do
|
|||
end
|
||||
end
|
||||
|
||||
defp handle_action(socket, {:start_evaluation, cell, _section}) do
|
||||
push_event(socket, "start_evaluation:#{cell.id}", %{})
|
||||
end
|
||||
|
||||
defp handle_action(socket, _action), do: socket
|
||||
|
||||
defp client_info(id, user) do
|
||||
|
|
@ -2229,6 +2244,9 @@ defmodule LivebookWeb.SessionLive do
|
|||
|
||||
data_view
|
||||
|
||||
{:add_cell_evaluation_output, _client_id, _cell_id, {:doctest_result, _result}} ->
|
||||
data_view
|
||||
|
||||
{:add_cell_evaluation_output, _client_id, cell_id, {:stdout, text}} ->
|
||||
# Lookup in previous data to see if the output is already there
|
||||
case Notebook.fetch_cell_and_section(prev_data.notebook, cell_id) do
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue