-
Notifications
You must be signed in to change notification settings - Fork 323
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
Copy-pasting nodes #7618
Merged
Merged
Copy-pasting nodes #7618
Changes from 11 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
f459a82
Draft implementation of copying
vitvakatu ceffe4f
Draft implementation of pasting done
vitvakatu bfd6065
Custom clipboard content format
vitvakatu 44fefc8
Copy-paste node metadata
vitvakatu 4453032
Use cursor position for placing pasted nodes
vitvakatu 7717e72
update shortcuts.md
vitvakatu a86e724
Properly support undo-redo when pasting
vitvakatu 353d5c6
Improve api
vitvakatu 29be896
Implement plain text fallback
vitvakatu 49559d8
Polishing
vitvakatu c625edc
Self-review
vitvakatu d84ffd8
Update lib/rust/web/js/clipboard.js
vitvakatu 5d37a5f
Implement pasting as plain text to external programs
vitvakatu a641f90
Add changelog entry
vitvakatu d5c421e
Fix pasted node position
vitvakatu e38f190
Simplify code
vitvakatu 1cb7152
Display user-facing error message when pasting fails
vitvakatu d60987b
Align nodes to nearest on paste
vitvakatu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
//! Copy-pasting nodes using the clipboard. | ||
//! | ||
//! # Clipboard Content Format | ||
//! | ||
//! We use a JSON-encoded [`ClipboardContent`] structure, marked with our custom [`MIME_TYPE`]. | ||
//! This way, we have a separate clipboard format for our application and can extend it in the | ||
//! future. | ||
//! We also support plain text pasting to make it easier to paste the content from other | ||
//! applications, but only if the [`PLAIN_TEXT_PASTING_ENABLED`] is `true`. Allowing pasting plain | ||
//! text can bring unnecessary security risks, like the execution of malicious code immediately | ||
//! after pasting. | ||
//! | ||
//! To copy the node as plain text, the user can enter the editing node, select the node expression, | ||
//! and copy it to the clipboard using the [`ensogl::Text`] functionality. | ||
|
||
use crate::prelude::*; | ||
|
||
use crate::controller::graph::Handle; | ||
use crate::controller::graph::NewNodeInfo; | ||
use crate::model::module::NodeMetadata; | ||
|
||
use ensogl::system::web::clipboard; | ||
use serde::Deserialize; | ||
use serde::Serialize; | ||
|
||
|
||
|
||
/// We use the `web` prefix to be able to use a custom MIME type. Typically browsers support a | ||
/// restricted set of MIME types in the clipboard. | ||
/// See [Clipboard pickling](https://github.com/w3c/editing/blob/gh-pages/docs/clipboard-pickling/explainer.md). | ||
/// | ||
/// `application/enso` is not an officially registered MIME-type (yet), but it is not important for | ||
/// our purposes. | ||
const MIME_TYPE: &str = "web application/enso"; | ||
/// Whether to allow pasting nodes from plain text. | ||
const PLAIN_TEXT_PASTING_ENABLED: bool = true; | ||
|
||
/// Clipboard payload. | ||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | ||
enum ClipboardContent { | ||
/// A single node that was copied from the application. | ||
Node(CopiedNode), | ||
} | ||
|
||
/// A single node that was copied from the application. | ||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | ||
struct CopiedNode { | ||
/// A whole node's expression (without a pattern). | ||
expression: String, | ||
/// Node's metadata. | ||
metadata: Option<NodeMetadata>, | ||
} | ||
|
||
/// Copy the node to the clipboard. | ||
pub fn copy_node(expression: String, metadata: Option<NodeMetadata>) -> FallibleResult { | ||
let content = ClipboardContent::Node(CopiedNode { expression, metadata }); | ||
let text_repr = serde_json::to_string(&content)?; | ||
clipboard::write(text_repr.as_bytes(), MIME_TYPE.to_string()); | ||
Ok(()) | ||
} | ||
|
||
|
||
/// Paste the node from the clipboard at a specific position. | ||
pub fn paste_node(graph: &Handle, position: Vector2) -> FallibleResult { | ||
clipboard::read( | ||
MIME_TYPE.to_string(), | ||
paste_node_from_custom_format(graph, position), | ||
plain_text_fallback(graph, position), | ||
); | ||
Ok(()) | ||
} | ||
|
||
/// A standard callback for pasting node using our custom format. | ||
fn paste_node_from_custom_format(graph: &Handle, position: Vector2) -> impl Fn(Vec<u8>) + 'static { | ||
let graph = graph.clone_ref(); | ||
move |content| { | ||
let _transaction = graph.module.get_or_open_transaction("Paste node"); | ||
let string = String::from_utf8(content).unwrap(); | ||
if let Ok(content) = serde_json::from_str(&string) { | ||
match content { | ||
ClipboardContent::Node(node) => { | ||
let expression = node.expression; | ||
let metadata = node.metadata; | ||
if let Err(err) = graph.new_node_at_position(position, expression, metadata) { | ||
error!("Failed to paste node. {err}"); | ||
} | ||
} | ||
} | ||
} else { | ||
error!( | ||
"`application/enso` MIME-type is used, but clipboard content has incorrect format." | ||
); | ||
} | ||
} | ||
} | ||
|
||
/// An alternative callback for pasting node from plain text. It is used when [`MIME_TYPE`] is not | ||
/// available in the clipboard, and only if [`PLAIN_TEXT_PASTING_ENABLED`]. Otherwise, it is a | ||
/// noop. | ||
fn plain_text_fallback(graph: &Handle, position: Vector2) -> impl Fn(String) + 'static { | ||
let graph = graph.clone_ref(); | ||
move |text| { | ||
if PLAIN_TEXT_PASTING_ENABLED { | ||
let _transaction = graph.module.get_or_open_transaction("Paste node"); | ||
let expression = text; | ||
if let Err(err) = graph.new_node_at_position(position, expression, None) { | ||
error!("Failed to paste node. {err}"); | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
|
||
// =============== | ||
// === Helpers === | ||
// =============== | ||
|
||
impl Handle { | ||
/// Create a new node at the provided position. | ||
fn new_node_at_position( | ||
&self, | ||
position: Vector2, | ||
expression: String, | ||
metadata: Option<NodeMetadata>, | ||
) -> FallibleResult { | ||
let info = NewNodeInfo { | ||
expression, | ||
doc_comment: None, | ||
metadata, | ||
id: None, | ||
location_hint: double_representation::graph::LocationHint::End, | ||
introduce_pattern: true, | ||
}; | ||
let ast_id = self.add_node(info)?; | ||
self.set_node_position(ast_id, position)?; | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docs accurate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, it's still not typical to use it, but in this case we need it.