Compare commits
3 commits
main
...
action-inp
Author | SHA1 | Date | |
---|---|---|---|
![]() |
97ff79138a | ||
![]() |
b2805e4559 | ||
![]() |
b788549556 |
7 changed files with 377 additions and 43 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -14676,6 +14676,7 @@ dependencies = [
|
||||||
"search",
|
"search",
|
||||||
"serde",
|
"serde",
|
||||||
"settings",
|
"settings",
|
||||||
|
"tempfile",
|
||||||
"theme",
|
"theme",
|
||||||
"tree-sitter-json",
|
"tree-sitter-json",
|
||||||
"tree-sitter-rust",
|
"tree-sitter-rust",
|
||||||
|
|
|
@ -216,7 +216,7 @@ impl JsonLspAdapter {
|
||||||
paths::local_debug_file_relative_path()
|
paths::local_debug_file_relative_path()
|
||||||
],
|
],
|
||||||
"schema": debug_schema,
|
"schema": debug_schema,
|
||||||
},
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
|
|
|
@ -1242,6 +1242,18 @@ impl LanguageServer {
|
||||||
params,
|
params,
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
eprintln!("{}", {
|
||||||
|
let value = serde_json::from_str::<serde_json::Value>(&message).unwrap();
|
||||||
|
if !value
|
||||||
|
.get("method")
|
||||||
|
.and_then(|method| method.as_str())
|
||||||
|
.map_or(false, |method| method.starts_with("json"))
|
||||||
|
{
|
||||||
|
"other".to_string()
|
||||||
|
} else {
|
||||||
|
serde_json::to_string_pretty(&value).unwrap()
|
||||||
|
}
|
||||||
|
});
|
||||||
outbound_tx.try_send(message)?;
|
outbound_tx.try_send(message)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod clangd_ext;
|
pub mod clangd_ext;
|
||||||
|
pub mod json_language_server_ext;
|
||||||
pub mod lsp_ext_command;
|
pub mod lsp_ext_command;
|
||||||
pub mod rust_analyzer_ext;
|
pub mod rust_analyzer_ext;
|
||||||
|
|
||||||
|
|
147
crates/project/src/lsp_store/json_language_server_ext.rs
Normal file
147
crates/project/src/lsp_store/json_language_server_ext.rs
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
use ::serde::{Deserialize, Serialize};
|
||||||
|
use gpui::{App, Entity, WeakEntity};
|
||||||
|
use language::Buffer;
|
||||||
|
use language::{File as _, LocalFile as _};
|
||||||
|
use lsp::{DidCloseTextDocumentParams, DidOpenTextDocumentParams, LanguageServer};
|
||||||
|
use util::ResultExt as _;
|
||||||
|
|
||||||
|
use crate::{LspStore, Project};
|
||||||
|
|
||||||
|
// https://github.com/microsoft/vscode/blob/main/extensions/json-language-features/server/README.md#schema-associations-notification
|
||||||
|
struct SchemaAssociationsNotification {}
|
||||||
|
|
||||||
|
/// interface ISchemaAssociation {
|
||||||
|
/// /**
|
||||||
|
/// * The URI of the schema, which is also the identifier of the schema.
|
||||||
|
/// */
|
||||||
|
/// uri: string;
|
||||||
|
///
|
||||||
|
/// /**
|
||||||
|
/// * A list of file path patterns that are associated to the schema. The '*' wildcard can be used. Exclusion patterns starting with '!'.
|
||||||
|
/// * For example '*.schema.json', 'package.json', '!foo*.schema.json'.
|
||||||
|
/// * A match succeeds when there is at least one pattern matching and last matching pattern does not start with '!'.
|
||||||
|
/// */
|
||||||
|
/// fileMatch: string[];
|
||||||
|
/// /**
|
||||||
|
/// * If provided, the association is only used if the validated document is located in the given folder (directly or in a subfolder)
|
||||||
|
/// */
|
||||||
|
/// folderUri?: string;
|
||||||
|
/// /*
|
||||||
|
/// * The schema for the given URI.
|
||||||
|
/// * If no schema is provided, the schema will be fetched with the schema request service (if available).
|
||||||
|
/// */
|
||||||
|
/// schema?: JSONSchema;
|
||||||
|
/// }
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct SchemaAssociation {
|
||||||
|
pub uri: String,
|
||||||
|
pub file_match: Vec<String>,
|
||||||
|
pub folder_uri: Option<String>,
|
||||||
|
pub schema: Option<serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl lsp::notification::Notification for SchemaAssociationsNotification {
|
||||||
|
type Params = Vec<SchemaAssociation>;
|
||||||
|
const METHOD: &'static str = "json/schemaAssociations";
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_schema_associations_notification(
|
||||||
|
project: Entity<Project>,
|
||||||
|
buffer: Entity<Buffer>,
|
||||||
|
schema_associations: &Vec<SchemaAssociation>,
|
||||||
|
cx: &mut App,
|
||||||
|
) {
|
||||||
|
let lsp_store = project.read(cx).lsp_store();
|
||||||
|
lsp_store.update(cx, |lsp_store, cx| {
|
||||||
|
let Some(local) = lsp_store.as_local_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
buffer.update(cx, |buffer, cx| {
|
||||||
|
for (adapter, server) in local
|
||||||
|
.language_servers_for_buffer(buffer, cx)
|
||||||
|
.map(|(a, b)| (a.clone(), b.clone()))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
{
|
||||||
|
if dbg!(!adapter.adapter.is_primary_zed_json_schema_adapter()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
server
|
||||||
|
.notify::<SchemaAssociationsNotification>(schema_associations)
|
||||||
|
.log_err(); // todo! don't ignore error
|
||||||
|
|
||||||
|
let file = match worktree::File::from_dyn(buffer.file()) {
|
||||||
|
Some(file) => file,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let language = match buffer.language() {
|
||||||
|
Some(language) => language,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let uri = lsp::Url::from_file_path(file.abs_path(cx)).unwrap();
|
||||||
|
|
||||||
|
let versions = local
|
||||||
|
.buffer_snapshots
|
||||||
|
.entry(buffer.remote_id())
|
||||||
|
.or_default()
|
||||||
|
.entry(server.server_id())
|
||||||
|
// .and_modify(|_| {
|
||||||
|
// assert!(
|
||||||
|
// false,
|
||||||
|
// "There should not be an existing snapshot for a newly inserted buffer"
|
||||||
|
// )
|
||||||
|
// })
|
||||||
|
.or_insert_with(|| {
|
||||||
|
vec![crate::lsp_store::LspBufferSnapshot {
|
||||||
|
version: 0,
|
||||||
|
snapshot: buffer.text_snapshot(),
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
let snapshot = versions.last().unwrap();
|
||||||
|
let version = snapshot.version;
|
||||||
|
let initial_snapshot = &snapshot.snapshot;
|
||||||
|
|
||||||
|
// if file.worktree.read(cx).id() != key.0
|
||||||
|
// || !self
|
||||||
|
// .languages
|
||||||
|
// .lsp_adapters(&language.name())
|
||||||
|
// .iter()
|
||||||
|
// .any(|a| a.name == key.1)
|
||||||
|
// {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// didOpen
|
||||||
|
let file = match file.as_local() {
|
||||||
|
Some(file) => file,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
let Some(_) = server
|
||||||
|
.notify::<lsp::notification::DidCloseTextDocument>(
|
||||||
|
&DidCloseTextDocumentParams {
|
||||||
|
text_document: lsp::TextDocumentIdentifier { uri: uri.clone() },
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.log_err()
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let initial_text = buffer.text();
|
||||||
|
|
||||||
|
server
|
||||||
|
.notify::<lsp::notification::DidOpenTextDocument>(&DidOpenTextDocumentParams {
|
||||||
|
text_document: lsp::TextDocumentItem::new(
|
||||||
|
uri,
|
||||||
|
adapter.language_id(&language.name()),
|
||||||
|
version,
|
||||||
|
initial_text,
|
||||||
|
),
|
||||||
|
})
|
||||||
|
.log_err();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ schemars.workspace = true
|
||||||
search.workspace = true
|
search.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
settings.workspace = true
|
settings.workspace = true
|
||||||
|
tempfile.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
tree-sitter-json.workspace = true
|
tree-sitter-json.workspace = true
|
||||||
tree-sitter-rust.workspace = true
|
tree-sitter-rust.workspace = true
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
ops::{Not, Range},
|
ops::{Not, Range},
|
||||||
|
path::PathBuf,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,6 +16,8 @@ use gpui::{
|
||||||
StyledText, Subscription, WeakEntity, actions, div,
|
StyledText, Subscription, WeakEntity, actions, div,
|
||||||
};
|
};
|
||||||
use language::{Language, LanguageConfig, ToOffset as _};
|
use language::{Language, LanguageConfig, ToOffset as _};
|
||||||
|
use project::Project;
|
||||||
|
use schemars::JsonSchema as _;
|
||||||
use settings::{BaseKeymap, KeybindSource, KeymapFile, SettingsAssets};
|
use settings::{BaseKeymap, KeybindSource, KeymapFile, SettingsAssets};
|
||||||
|
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
@ -62,6 +65,7 @@ pub fn init(cx: &mut App) {
|
||||||
|
|
||||||
cx.on_action(|_: &OpenKeymapEditor, cx| {
|
cx.on_action(|_: &OpenKeymapEditor, cx| {
|
||||||
workspace::with_active_or_new_workspace(cx, move |workspace, window, cx| {
|
workspace::with_active_or_new_workspace(cx, move |workspace, window, cx| {
|
||||||
|
// todo! with_local_workspace
|
||||||
let existing = workspace
|
let existing = workspace
|
||||||
.active_pane()
|
.active_pane()
|
||||||
.read(cx)
|
.read(cx)
|
||||||
|
@ -394,7 +398,9 @@ impl KeymapEditor {
|
||||||
action_name: action_name.into(),
|
action_name: action_name.into(),
|
||||||
action_input,
|
action_input,
|
||||||
action_docs,
|
action_docs,
|
||||||
action_schema: action_schema.get(action_name).cloned(),
|
action_schema: action_schema.get(action_name).map(|action_schema| {
|
||||||
|
root_schema_from_action_schema(action_schema, &mut generator)
|
||||||
|
}),
|
||||||
context: Some(context),
|
context: Some(context),
|
||||||
source,
|
source,
|
||||||
});
|
});
|
||||||
|
@ -411,7 +417,9 @@ impl KeymapEditor {
|
||||||
action_name: action_name.into(),
|
action_name: action_name.into(),
|
||||||
action_input: None,
|
action_input: None,
|
||||||
action_docs: action_documentation.get(action_name).copied(),
|
action_docs: action_documentation.get(action_name).copied(),
|
||||||
action_schema: action_schema.get(action_name).cloned(),
|
action_schema: action_schema.get(action_name).map(|action_schema| {
|
||||||
|
root_schema_from_action_schema(action_schema, &mut generator)
|
||||||
|
}),
|
||||||
context: None,
|
context: None,
|
||||||
source: None,
|
source: None,
|
||||||
});
|
});
|
||||||
|
@ -424,7 +432,13 @@ impl KeymapEditor {
|
||||||
fn update_keybindings(&mut self, cx: &mut Context<KeymapEditor>) {
|
fn update_keybindings(&mut self, cx: &mut Context<KeymapEditor>) {
|
||||||
let workspace = self.workspace.clone();
|
let workspace = self.workspace.clone();
|
||||||
cx.spawn(async move |this, cx| {
|
cx.spawn(async move |this, cx| {
|
||||||
let json_language = load_json_language(workspace.clone(), cx).await;
|
let json_language = load_json_language(
|
||||||
|
workspace
|
||||||
|
.read_with(cx, |workspace, _cx| workspace.project().downgrade())
|
||||||
|
.ok(),
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
let rust_language = load_rust_language(workspace.clone(), cx).await;
|
let rust_language = load_rust_language(workspace.clone(), cx).await;
|
||||||
|
|
||||||
let query = this.update(cx, |this, cx| {
|
let query = this.update(cx, |this, cx| {
|
||||||
|
@ -583,17 +597,27 @@ impl KeymapEditor {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let keymap_editor = cx.entity();
|
let keymap_editor = cx.entity();
|
||||||
|
let Some((fs, project)) = self
|
||||||
|
.workspace
|
||||||
|
.read_with(cx, |workspace, _| {
|
||||||
|
(
|
||||||
|
workspace.app_state().fs.clone(),
|
||||||
|
workspace.project().clone(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
self.workspace
|
self.workspace
|
||||||
.update(cx, |workspace, cx| {
|
.update(cx, |workspace, cx| {
|
||||||
let fs = workspace.app_state().fs.clone();
|
|
||||||
let workspace_weak = cx.weak_entity();
|
|
||||||
workspace.toggle_modal(window, cx, |window, cx| {
|
workspace.toggle_modal(window, cx, |window, cx| {
|
||||||
let modal = KeybindingEditorModal::new(
|
let modal = KeybindingEditorModal::new(
|
||||||
create,
|
create,
|
||||||
keybind,
|
keybind,
|
||||||
keybind_idx,
|
keybind_idx,
|
||||||
keymap_editor,
|
keymap_editor,
|
||||||
workspace_weak,
|
project,
|
||||||
fs,
|
fs,
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|
@ -971,6 +995,10 @@ struct KeybindingEditorModal {
|
||||||
keybind_editor: Entity<KeystrokeInput>,
|
keybind_editor: Entity<KeystrokeInput>,
|
||||||
context_editor: Entity<Editor>,
|
context_editor: Entity<Editor>,
|
||||||
input_editor: Option<Entity<Editor>>,
|
input_editor: Option<Entity<Editor>>,
|
||||||
|
_input_editor_data: (
|
||||||
|
Option<Entity<Entity<language::Buffer>>>,
|
||||||
|
Option<tempfile::TempDir>,
|
||||||
|
),
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
error: Option<InputError>,
|
error: Option<InputError>,
|
||||||
keymap_editor: Entity<KeymapEditor>,
|
keymap_editor: Entity<KeymapEditor>,
|
||||||
|
@ -992,10 +1020,10 @@ impl KeybindingEditorModal {
|
||||||
editing_keybind: ProcessedKeybinding,
|
editing_keybind: ProcessedKeybinding,
|
||||||
editing_keybind_idx: usize,
|
editing_keybind_idx: usize,
|
||||||
keymap_editor: Entity<KeymapEditor>,
|
keymap_editor: Entity<KeymapEditor>,
|
||||||
workspace: WeakEntity<Workspace>,
|
project: Entity<Project>,
|
||||||
fs: Arc<dyn Fs>,
|
fs: Arc<dyn Fs>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut Context<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let keybind_editor = cx.new(|cx| KeystrokeInput::new(window, cx));
|
let keybind_editor = cx.new(|cx| KeystrokeInput::new(window, cx));
|
||||||
|
|
||||||
|
@ -1030,31 +1058,104 @@ impl KeybindingEditorModal {
|
||||||
editor
|
editor
|
||||||
});
|
});
|
||||||
|
|
||||||
let input_editor = editing_keybind.action_schema.clone().map(|_schema| {
|
if let Some(schema) = editing_keybind.action_schema.clone() {
|
||||||
cx.new(|cx| {
|
let project = project.downgrade();
|
||||||
let mut editor = Editor::auto_height_unbounded(1, window, cx);
|
let fs = fs.clone();
|
||||||
if let Some(input) = editing_keybind.action_input.clone() {
|
let file_name = file_name_for_action_input(&editing_keybind.action_name);
|
||||||
editor.set_text(input.text, window, cx);
|
let action_input = editing_keybind
|
||||||
} else {
|
.action_input
|
||||||
// TODO: default value from schema?
|
.as_ref()
|
||||||
editor.set_placeholder_text("Action input", cx);
|
.map(|input| input.text.clone());
|
||||||
}
|
cx.spawn_in(window, async move |this, cx| {
|
||||||
cx.spawn(async |editor, cx| {
|
// todo! fix when modal is dropped, buffer and temp_dir are dropped before worktree, resulting in worktree scan errors
|
||||||
let json_language = load_json_language(workspace, cx).await;
|
// being printed due to the non existant worktree
|
||||||
editor
|
let (buffer, temp_dir) = create_temp_buffer_for_action_input(file_name.clone(), project.clone(), fs, cx)
|
||||||
.update(cx, |editor, cx| {
|
.await
|
||||||
if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
|
.context("Failed to create temporary buffer for action input. Auto-complete will not work")
|
||||||
buffer.update(cx, |buffer, cx| {
|
.log_err()
|
||||||
buffer.set_language(Some(json_language), cx)
|
.unzip();
|
||||||
});
|
let buffer = match buffer {
|
||||||
|
Some(buffer) => buffer,
|
||||||
|
None => cx.new(|cx| language::Buffer::local("", cx))?
|
||||||
|
};
|
||||||
|
let open_lsp_handle =
|
||||||
|
project.update(cx, |project, cx| {
|
||||||
|
project.register_buffer_with_language_servers(&buffer, cx)
|
||||||
|
}).ok();
|
||||||
|
cx.spawn(
|
||||||
|
{
|
||||||
|
let project = project.clone();
|
||||||
|
let buffer = buffer.downgrade();
|
||||||
|
async move |cx| {
|
||||||
|
let json_language = load_json_language(Some(project), cx).await;
|
||||||
|
buffer
|
||||||
|
.update(cx, |buffer, cx| {
|
||||||
|
buffer.set_language(Some(json_language), cx)
|
||||||
|
})
|
||||||
|
.context(
|
||||||
|
"Failed to load JSON language for editing keybinding action input",
|
||||||
|
).log_err()
|
||||||
|
|
||||||
|
}}).detach();
|
||||||
|
|
||||||
|
cx.spawn({
|
||||||
|
let project = project.clone();
|
||||||
|
let buffer = buffer.downgrade();
|
||||||
|
|
||||||
|
|
||||||
|
async move |cx| {
|
||||||
|
cx.background_executor().timer(std::time::Duration::from_secs(10)).await;
|
||||||
|
let Some(project) = project.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(buffer) = buffer.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let uri = "lol://some.uri".into();
|
||||||
|
let schema_associations = vec![
|
||||||
|
project::lsp_store::json_language_server_ext::SchemaAssociation {
|
||||||
|
uri,
|
||||||
|
file_match: vec![file_name],
|
||||||
|
folder_uri: None,
|
||||||
|
schema: Some(schema.to_value()),
|
||||||
}
|
}
|
||||||
})
|
];
|
||||||
.context("Failed to load JSON language for editing keybinding action input")
|
cx.update(|_, cx| {
|
||||||
|
project::lsp_store::json_language_server_ext::send_schema_associations_notification(project, buffer, &schema_associations, cx);
|
||||||
|
}).ok();
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
|
|
||||||
|
let editor = cx.new_window_entity(|window, cx| {
|
||||||
|
let multi_buffer =
|
||||||
|
cx.new(|cx| editor::MultiBuffer::singleton(buffer.clone(), cx));
|
||||||
|
let mut editor = Editor::new(
|
||||||
|
editor::EditorMode::AutoHeight {
|
||||||
|
min_lines: 1,
|
||||||
|
max_lines: Some(10),
|
||||||
|
},
|
||||||
|
multi_buffer.clone(),
|
||||||
|
project.upgrade(),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
if let Some(input) = action_input {
|
||||||
|
editor.set_text(input, window, cx);
|
||||||
|
} else {
|
||||||
|
// TODO: default value from schema?
|
||||||
|
editor.set_placeholder_text("Action input", cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
editor
|
||||||
|
})?;
|
||||||
|
|
||||||
|
this.update(cx, |this, _cx| {
|
||||||
|
this.input_editor = Some(editor);
|
||||||
|
this._input_editor_data = (open_lsp_handle, temp_dir);
|
||||||
})
|
})
|
||||||
.detach_and_log_err(cx);
|
|
||||||
editor
|
|
||||||
})
|
})
|
||||||
});
|
.detach_and_log_err(cx);
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
creating: create,
|
creating: create,
|
||||||
|
@ -1063,7 +1164,8 @@ impl KeybindingEditorModal {
|
||||||
fs,
|
fs,
|
||||||
keybind_editor,
|
keybind_editor,
|
||||||
context_editor,
|
context_editor,
|
||||||
input_editor,
|
input_editor: None,
|
||||||
|
_input_editor_data: (None, None),
|
||||||
error: None,
|
error: None,
|
||||||
keymap_editor,
|
keymap_editor,
|
||||||
}
|
}
|
||||||
|
@ -1276,6 +1378,53 @@ impl Render for KeybindingEditorModal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn file_name_for_action_input(action_name: &SharedString) -> String {
|
||||||
|
let mut file_name = action_name.as_ref().replace("::", "_");
|
||||||
|
file_name.push_str(".json");
|
||||||
|
file_name
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_temp_buffer_for_action_input(
|
||||||
|
file_name: String,
|
||||||
|
project: WeakEntity<Project>,
|
||||||
|
fs: Arc<dyn Fs>,
|
||||||
|
cx: &mut AsyncApp,
|
||||||
|
) -> anyhow::Result<(Entity<language::Buffer>, tempfile::TempDir)> {
|
||||||
|
let (temp_file_path, temp_dir) = create_temp_file_for_action_input(file_name.clone(), fs)
|
||||||
|
.await
|
||||||
|
.context("Failed to create backing file")?;
|
||||||
|
|
||||||
|
project
|
||||||
|
.update(cx, |project, cx| {
|
||||||
|
project.open_local_buffer(temp_file_path, cx)
|
||||||
|
})?
|
||||||
|
.await
|
||||||
|
.context("Failed to create buffer")
|
||||||
|
.map(|buffer| (buffer, temp_dir))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_temp_file_for_action_input(
|
||||||
|
file_name: String,
|
||||||
|
fs: Arc<dyn Fs>,
|
||||||
|
) -> anyhow::Result<(PathBuf, tempfile::TempDir)> {
|
||||||
|
let temp_dir = paths::temp_dir();
|
||||||
|
let sub_temp_dir = tempfile::Builder::new()
|
||||||
|
.tempdir_in(temp_dir)
|
||||||
|
.context("Failed to create temporary directory")?;
|
||||||
|
|
||||||
|
let path = sub_temp_dir.path().join(file_name);
|
||||||
|
fs.create_file(
|
||||||
|
&path,
|
||||||
|
fs::CreateOptions {
|
||||||
|
ignore_if_exists: true,
|
||||||
|
overwrite: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("Failed to create temporary file")?;
|
||||||
|
Ok((path, sub_temp_dir))
|
||||||
|
}
|
||||||
|
|
||||||
struct KeyContextCompletionProvider {
|
struct KeyContextCompletionProvider {
|
||||||
contexts: Vec<SharedString>,
|
contexts: Vec<SharedString>,
|
||||||
}
|
}
|
||||||
|
@ -1339,17 +1488,18 @@ impl CompletionProvider for KeyContextCompletionProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn load_json_language(workspace: WeakEntity<Workspace>, cx: &mut AsyncApp) -> Arc<Language> {
|
async fn load_json_language(
|
||||||
let json_language_task = workspace
|
project: Option<WeakEntity<Project>>,
|
||||||
.read_with(cx, |workspace, cx| {
|
cx: &mut AsyncApp,
|
||||||
workspace
|
) -> Arc<Language> {
|
||||||
.project()
|
let json_language_task = project.and_then(|project| {
|
||||||
.read(cx)
|
project
|
||||||
.languages()
|
.read_with(cx, |project, _| {
|
||||||
.language_for_name("JSON")
|
project.languages().language_for_name("JSON")
|
||||||
})
|
})
|
||||||
.context("Failed to load JSON language")
|
.context("Failed to load JSON language")
|
||||||
.log_err();
|
.log_err()
|
||||||
|
});
|
||||||
let json_language = match json_language_task {
|
let json_language = match json_language_task {
|
||||||
Some(task) => task.await.context("Failed to load JSON language").log_err(),
|
Some(task) => task.await.context("Failed to load JSON language").log_err(),
|
||||||
None => None,
|
None => None,
|
||||||
|
@ -1458,6 +1608,28 @@ async fn save_keybinding_update(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn root_schema_from_action_schema(
|
||||||
|
action_schema: &schemars::Schema,
|
||||||
|
generator: &mut schemars::SchemaGenerator,
|
||||||
|
) -> schemars::Schema {
|
||||||
|
let meta_schema = generator
|
||||||
|
.settings()
|
||||||
|
.meta_schema
|
||||||
|
.as_ref()
|
||||||
|
.expect("meta_schema should be present in schemars settings")
|
||||||
|
.to_string();
|
||||||
|
let defs = generator.definitions();
|
||||||
|
let mut schema = schemars::json_schema!({
|
||||||
|
"$schema": meta_schema,
|
||||||
|
"allowTrailingCommas": true,
|
||||||
|
"$defs": defs,
|
||||||
|
});
|
||||||
|
schema
|
||||||
|
.ensure_object()
|
||||||
|
.extend(std::mem::take(action_schema.clone().ensure_object()).into_iter());
|
||||||
|
schema
|
||||||
|
}
|
||||||
|
|
||||||
struct KeystrokeInput {
|
struct KeystrokeInput {
|
||||||
keystrokes: Vec<Keystroke>,
|
keystrokes: Vec<Keystroke>,
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue