mirror of
https://github.com/livebook-dev/livebook.git
synced 2024-12-27 01:42:11 +08:00
Keep original source when highlighting (#480)
This commit is contained in:
parent
969b204b58
commit
3216727858
8 changed files with 87 additions and 40 deletions
|
@ -13,6 +13,10 @@ solely client-side operations.
|
|||
@apply hidden;
|
||||
}
|
||||
|
||||
[phx-hook="Highlight"][data-highlighted] > [data-source] {
|
||||
@apply hidden;
|
||||
}
|
||||
|
||||
/* === Session === */
|
||||
|
||||
[data-element="session"]:not([data-js-insert-mode])
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { getAttributeOrThrow } from "../lib/attribute";
|
||||
import { highlight } from "../cell/live_editor/monaco";
|
||||
import { findChildOrThrow } from "../lib/utils";
|
||||
|
||||
/**
|
||||
* A hook used to highlight source code in the root element.
|
||||
|
@ -7,18 +8,45 @@ import { highlight } from "../cell/live_editor/monaco";
|
|||
* Configuration:
|
||||
*
|
||||
* * `data-language` - language of the source code
|
||||
*
|
||||
* The element should have two children:
|
||||
*
|
||||
* * one annotated with `data-source` attribute, it should contain
|
||||
* the source code to be highlighted
|
||||
*
|
||||
* * one annotated with `data-target` where the highlighted code
|
||||
* is rendered
|
||||
*/
|
||||
const Highlight = {
|
||||
mounted() {
|
||||
this.props = getProps(this);
|
||||
this.state = {
|
||||
sourceElement: null,
|
||||
originalElement: null,
|
||||
};
|
||||
|
||||
highlightIn(this.el, this.props.language);
|
||||
this.state.sourceElement = findChildOrThrow(this.el, "[data-source]");
|
||||
this.state.targetElement = findChildOrThrow(this.el, "[data-target]");
|
||||
|
||||
highlightInto(
|
||||
this.state.targetElement,
|
||||
this.state.sourceElement,
|
||||
this.props.language
|
||||
).then(() => {
|
||||
this.el.setAttribute("data-highlighted", "true");
|
||||
});
|
||||
},
|
||||
|
||||
updated() {
|
||||
this.props = getProps(this);
|
||||
|
||||
highlightIn(this.el, this.props.language);
|
||||
highlightInto(
|
||||
this.state.targetElement,
|
||||
this.state.sourceElement,
|
||||
this.props.language
|
||||
).then(() => {
|
||||
this.el.setAttribute("data-highlighted", "true");
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -28,11 +56,11 @@ function getProps(hook) {
|
|||
};
|
||||
}
|
||||
|
||||
function highlightIn(element, language) {
|
||||
const code = element.innerText;
|
||||
function highlightInto(targetElement, sourceElement, language) {
|
||||
const code = sourceElement.innerText;
|
||||
|
||||
highlight(code, language).then((html) => {
|
||||
element.innerHTML = html;
|
||||
return highlight(code, language).then((html) => {
|
||||
targetElement.innerHTML = html;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -103,3 +103,15 @@ export function throttle(fn, windowMs) {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function findChildOrThrow(element, selector) {
|
||||
const child = element.querySelector(selector);
|
||||
|
||||
if (!child) {
|
||||
throw new Error(
|
||||
`expected a child matching ${selector}, but none was found`
|
||||
);
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
parseBoolean,
|
||||
parseInteger,
|
||||
} from "../lib/attribute";
|
||||
import { getLineHeight } from "../lib/utils";
|
||||
import { findChildOrThrow, getLineHeight } from "../lib/utils";
|
||||
|
||||
/**
|
||||
* A hook used to render text lines as a virtual list,
|
||||
|
@ -37,21 +37,8 @@ const VirtualizedLines = {
|
|||
|
||||
this.state.lineHeight = getLineHeight(this.el);
|
||||
|
||||
this.state.templateElement = this.el.querySelector("[data-template]");
|
||||
|
||||
if (!this.state.templateElement) {
|
||||
throw new Error(
|
||||
"VirtualizedLines must have a child with data-template attribute"
|
||||
);
|
||||
}
|
||||
|
||||
this.state.contentElement = this.el.querySelector("[data-content]");
|
||||
|
||||
if (!this.state.contentElement) {
|
||||
throw new Error(
|
||||
"VirtualizedLines must have a child with data-content attribute"
|
||||
);
|
||||
}
|
||||
this.state.templateElement = findChildOrThrow(this.el, "[data-template]");
|
||||
this.state.contentElement = findChildOrThrow(this.el, "[data-content]");
|
||||
|
||||
const config = hyperListConfig(
|
||||
this.state.contentElement,
|
||||
|
|
|
@ -214,4 +214,26 @@ defmodule LivebookWeb.Helpers do
|
|||
</button>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders a highlighted code snippet.
|
||||
|
||||
## Examples
|
||||
|
||||
<.code_preview
|
||||
source_id="my-snippet"
|
||||
language="elixir"
|
||||
source="System.version()" />
|
||||
"""
|
||||
def code_preview(assigns) do
|
||||
~H"""
|
||||
<div class="markdown">
|
||||
<pre><code
|
||||
class="tiny-scrollbar"
|
||||
id={"#{@source_id}-highlight"}
|
||||
phx-hook="Highlight"
|
||||
data-language={@language}><div id={@source_id} data-source><%= @source %></div><div data-target></div></code></pre>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
end
|
||||
|
|
|
@ -88,12 +88,10 @@ defmodule LivebookWeb.SessionLive.BinComponent do
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="markdown">
|
||||
<pre><code
|
||||
id={"bin-cell-#{cell.id}-source"}
|
||||
phx-hook="Highlight"
|
||||
data-language={Cell.type(cell)}><%= cell.source %></code></pre>
|
||||
</div>
|
||||
<.code_preview
|
||||
source_id={"bin-cell-#{cell.id}-source"}
|
||||
language={Cell.type(cell)}
|
||||
source={cell.source} />
|
||||
</div>
|
||||
<% end %>
|
||||
<%= if length(@matching_entries) > @limit do %>
|
||||
|
|
|
@ -47,13 +47,10 @@ defmodule LivebookWeb.SessionLive.ExportElixirComponent do
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="markdown">
|
||||
<pre><code
|
||||
class="tiny-scrollbar"
|
||||
id="export-notebook-source"
|
||||
phx-hook="Highlight"
|
||||
data-language="elixir"><%= @source %></code></pre>
|
||||
</div>
|
||||
<.code_preview
|
||||
source_id="export-notebook-source"
|
||||
language="elixir"
|
||||
source={@source} />
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
|
|
@ -37,11 +37,10 @@ defmodule LivebookWeb.SessionLive.ExportLiveMarkdownComponent do
|
|||
</div>
|
||||
</div>
|
||||
<div class="markdown">
|
||||
<pre><code
|
||||
class="tiny-scrollbar"
|
||||
id="export-notebook-source"
|
||||
phx-hook="Highlight"
|
||||
data-language="markdown"><%= @source %></code></pre>
|
||||
<.code_preview
|
||||
source_id="export-notebook-source"
|
||||
language="markdown"
|
||||
source={@source} />
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
|
Loading…
Reference in a new issue