Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add doctest decorations to monaco editor per result #1911

Merged
merged 7 commits into from
May 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions assets/css/editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
22 changes: 22 additions & 0 deletions assets/js/hooks/cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
);
}
}
},
Expand Down
46 changes: 46 additions & 0 deletions assets/js/hooks/cell_editor/live_editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -270,6 +279,9 @@ class LiveEditor {
: "off",
});

this._doctestDecorations.decorationCollection =
this.editor.createDecorationsCollection([]);

this.editor.addAction({
contextMenuGroupId: "word-wrapping",
id: "enable-word-wrapping",
Expand Down Expand Up @@ -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) {
Expand Down
25 changes: 24 additions & 1 deletion lib/livebook/runtime/evaluator/doctests.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand All @@ -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
Expand Down
13 changes: 13 additions & 0 deletions lib/livebook/session/data.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions lib/livebook_web/live/session_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down