diff --git a/node/src/documents.rs b/node/src/documents.rs index d84d1448e4..769bc4a034 100644 --- a/node/src/documents.rs +++ b/node/src/documents.rs @@ -19,7 +19,7 @@ pub fn create(mut cx: FunctionContext) -> JsResult { let path = not_empty_or_none(&cx.argument::(0)?.value(&mut cx)); let format = not_empty_or_none(&cx.argument::(1)?.value(&mut cx)); - let result = RUNTIME.block_on(async { DOCUMENTS.create(path, format).await }); + let result = RUNTIME.block_on(async { DOCUMENTS.create(path, None, format).await }); to_json_or_throw(cx, result) } diff --git a/rust/stencila/src/documents.rs b/rust/stencila/src/documents.rs index 64c857fd73..2469cf2883 100644 --- a/rust/stencila/src/documents.rs +++ b/rust/stencila/src/documents.rs @@ -506,6 +506,22 @@ impl Document { } } + /// Create a new document, optionally with content. + pub async fn create>( + path: Option

, + content: Option, + format: Option, + ) -> Result { + let path = path.map(|path| PathBuf::from(path.as_ref())); + + let mut document = Document::new(path, format); + if let Some(content) = content { + document.load(content, None).await?; + } + + Ok(document) + } + /// Open a document from an existing file. /// /// # Arguments @@ -1693,11 +1709,14 @@ impl Documents { Ok(paths) } - /// Create a new empty document - pub async fn create(&self, path: Option, format: Option) -> Result { - let path = path.map(PathBuf::from); - - let document = Document::new(path, format); + /// Create a new document + pub async fn create>( + &self, + path: Option

, + content: Option, + format: Option, + ) -> Result { + let document = Document::create(path, content, format).await?; let document_id = document.id.clone(); let document_repr = document.repr(); let handler = DocumentHandler::new(document, false); diff --git a/rust/stencila/src/rpc.rs b/rust/stencila/src/rpc.rs index 63f107ed6e..47e1d23a35 100644 --- a/rust/stencila/src/rpc.rs +++ b/rust/stencila/src/rpc.rs @@ -54,6 +54,7 @@ impl Request { "sessions.subscribe" => sessions_subscribe(&self.params, client).await, "sessions.unsubscribe" => sessions_unsubscribe(&self.params, client).await, "kernels.languages" => kernels_languages(&self.params).await, + "documents.create" => documents_create(&self.params).await, "documents.open" => documents_open(&self.params).await, "documents.close" => documents_close(&self.params).await, "documents.patch" => documents_patch(&self.params).await, @@ -282,6 +283,15 @@ async fn kernels_languages(params: &Params) -> Result<(serde_json::Value, Subscr Ok((json!(kernels), Subscription::None)) } +async fn documents_create(params: &Params) -> Result<(serde_json::Value, Subscription)> { + let path = optional_string(params, "path")?; + let content = optional_string(params, "content")?; + let format = optional_string(params, "format")?; + + let document = DOCUMENTS.create(path, content, format).await?; + Ok((json!(document), Subscription::None)) +} + async fn documents_open(params: &Params) -> Result<(serde_json::Value, Subscription)> { let path = required_string(params, "path")?; diff --git a/web/src/documents.ts b/web/src/documents.ts index 36ce332180..0092824729 100644 --- a/web/src/documents.ts +++ b/web/src/documents.ts @@ -64,6 +64,21 @@ export interface ContentChangeEvent extends CustomEvent { detail: ViewUpdate | string } +/** + * Create a new document + * + * Optionally pass the content, in some format, for the new document. + */ +export async function create( + client: Client, + content?: string, + format?: string +): Promise { + return client.call('documents.create', { + content, format, + }) as Promise +} + /** * Open a document * @@ -425,15 +440,15 @@ async function onContentChange( */ export interface ValidatorChangeEvent extends CustomEvent { detail: - | { - type: 'property' - name: string - value: string - } - | { - type: 'validator' - value: Exclude - } + | { + type: 'property' + name: string + value: string + } + | { + type: 'validator' + value: Exclude + } } /** @@ -450,16 +465,16 @@ async function onValidatorChange( const [address, value]: [Address, JsonValue] = event.detail.type === 'property' ? // The new validator property value + [ [ - [ - // ...except for `default` which is actually a property of the parent parameter - ...(event.detail.name === 'default' ? [] : ['validator']), - event.detail.name, - ], - event.detail.value, - ] + // ...except for `default` which is actually a property of the parent parameter + ...(event.detail.name === 'default' ? [] : ['validator']), + event.detail.name, + ], + event.detail.value, + ] : // The new validator as an object with `type` - [['validator'], { type: event.detail.value }] + [['validator'], { type: event.detail.value }] const op: Operation = { type: 'Replace',