mirror of https://github.com/lapce/lapce.git
Merge pull request #666 from MinusGix/workspace-symbol-search
Impl searching for symbols in workspace
This commit is contained in:
commit
fc9eabdd1e
|
@ -226,6 +226,10 @@ command = "document_end"
|
|||
key = "meta+O"
|
||||
command = "palette.symbol"
|
||||
|
||||
[[keymaps]]
|
||||
key = "meta+t"
|
||||
command = "palette.workspace_symbol"
|
||||
|
||||
[[keymaps]]
|
||||
key = "ctrl+g"
|
||||
command = "palette.line"
|
||||
|
|
|
@ -221,6 +221,10 @@ command = "document_end"
|
|||
key = "ctrl+O"
|
||||
command = "palette.symbol"
|
||||
|
||||
[[keymaps]]
|
||||
key = "ctrl+t"
|
||||
command = "palette.workspace_symbol"
|
||||
|
||||
[[keymaps]]
|
||||
key = "ctrl+g"
|
||||
command = "palette.line"
|
||||
|
|
|
@ -271,6 +271,9 @@ pub enum LapceWorkbenchCommand {
|
|||
#[strum(serialize = "palette.symbol")]
|
||||
PaletteSymbol,
|
||||
|
||||
#[strum(serialize = "palette.workspace_symbol")]
|
||||
PaletteWorkspaceSymbol,
|
||||
|
||||
#[strum(message = "Command Palette")]
|
||||
#[strum(serialize = "palette.command")]
|
||||
PaletteCommand,
|
||||
|
|
|
@ -1100,6 +1100,13 @@ pub fn run_workbench_command(
|
|||
Target::Widget(self.palette.widget_id),
|
||||
));
|
||||
}
|
||||
LapceWorkbenchCommand::PaletteWorkspaceSymbol => {
|
||||
ctx.submit_command(Command::new(
|
||||
LAPCE_UI_COMMAND,
|
||||
LapceUICommand::RunPalette(Some(PaletteType::WorkspaceSymbol)),
|
||||
Target::Widget(self.palette.widget_id),
|
||||
));
|
||||
}
|
||||
LapceWorkbenchCommand::PaletteCommand => {
|
||||
ctx.submit_command(Command::new(
|
||||
LAPCE_UI_COMMAND,
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
use lapce_core::command::{EditCommand, FocusCommand};
|
||||
use lapce_core::mode::Mode;
|
||||
use lapce_core::movement::Movement;
|
||||
use lsp_types::{DocumentSymbolResponse, Range, SymbolKind};
|
||||
use lsp_types::{DocumentSymbolResponse, Range, SymbolInformation, SymbolKind};
|
||||
use serde_json;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashSet;
|
||||
|
@ -21,6 +21,7 @@
|
|||
use crate::data::{LapceWorkspace, LapceWorkspaceType};
|
||||
use crate::document::BufferContent;
|
||||
use crate::editor::EditorLocation;
|
||||
use crate::proxy::path_from_url;
|
||||
use crate::{
|
||||
command::LAPCE_UI_COMMAND,
|
||||
command::{CommandExecuted, LAPCE_COMMAND},
|
||||
|
@ -39,6 +40,7 @@ pub enum PaletteType {
|
|||
Line,
|
||||
GlobalSearch,
|
||||
DocumentSymbol,
|
||||
WorkspaceSymbol,
|
||||
Workspace,
|
||||
Command,
|
||||
Reference,
|
||||
|
@ -52,6 +54,7 @@ fn string(&self) -> String {
|
|||
PaletteType::File => "".to_string(),
|
||||
PaletteType::Line => "/".to_string(),
|
||||
PaletteType::DocumentSymbol => "@".to_string(),
|
||||
PaletteType::WorkspaceSymbol => "#".to_string(),
|
||||
PaletteType::GlobalSearch => "?".to_string(),
|
||||
PaletteType::Workspace => ">".to_string(),
|
||||
PaletteType::Command => ":".to_string(),
|
||||
|
@ -66,6 +69,7 @@ pub fn has_preview(&self) -> bool {
|
|||
self,
|
||||
PaletteType::Line
|
||||
| PaletteType::DocumentSymbol
|
||||
| PaletteType::WorkspaceSymbol
|
||||
| PaletteType::GlobalSearch
|
||||
| PaletteType::Reference
|
||||
)
|
||||
|
@ -97,6 +101,13 @@ pub enum PaletteItemContent {
|
|||
range: Range,
|
||||
container_name: Option<String>,
|
||||
},
|
||||
WorkspaceSymbol {
|
||||
// TODO: Include what language it is from?
|
||||
kind: SymbolKind,
|
||||
name: String,
|
||||
container_name: Option<String>,
|
||||
location: EditorLocation,
|
||||
},
|
||||
ReferenceLocation(PathBuf, EditorLocation),
|
||||
Workspace(LapceWorkspace),
|
||||
SshHost(String, String),
|
||||
|
@ -133,6 +144,18 @@ fn select(
|
|||
Target::Auto,
|
||||
));
|
||||
}
|
||||
PaletteItemContent::WorkspaceSymbol { location, .. } => {
|
||||
let editor_id = if preview {
|
||||
Some(preview_editor_id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
ctx.submit_command(Command::new(
|
||||
LAPCE_UI_COMMAND,
|
||||
LapceUICommand::JumpToLocation(editor_id, location.clone()),
|
||||
Target::Auto,
|
||||
));
|
||||
}
|
||||
PaletteItemContent::Line(line, _) => {
|
||||
let editor_id = if preview {
|
||||
Some(preview_editor_id)
|
||||
|
@ -430,6 +453,7 @@ pub fn get_input(&self) -> &str {
|
|||
PaletteType::SshHost => &self.input,
|
||||
PaletteType::Line => &self.input[1..],
|
||||
PaletteType::DocumentSymbol => &self.input[1..],
|
||||
PaletteType::WorkspaceSymbol => &self.input[1..],
|
||||
PaletteType::Workspace => &self.input[1..],
|
||||
PaletteType::Command => &self.input[1..],
|
||||
PaletteType::GlobalSearch => &self.input[1..],
|
||||
|
@ -461,7 +485,7 @@ pub fn run_references(
|
|||
ctx: &mut EventCtx,
|
||||
locations: &[EditorLocation],
|
||||
) {
|
||||
self.run(ctx, Some(PaletteType::Reference));
|
||||
self.run(ctx, Some(PaletteType::Reference), None);
|
||||
let items: Vec<PaletteItem> = locations
|
||||
.iter()
|
||||
.map(|l| {
|
||||
|
@ -487,11 +511,16 @@ pub fn run_references(
|
|||
palette.preview(ctx);
|
||||
}
|
||||
|
||||
pub fn run(&mut self, ctx: &mut EventCtx, palette_type: Option<PaletteType>) {
|
||||
pub fn run(
|
||||
&mut self,
|
||||
ctx: &mut EventCtx,
|
||||
palette_type: Option<PaletteType>,
|
||||
input: Option<String>,
|
||||
) {
|
||||
let palette = Arc::make_mut(&mut self.palette);
|
||||
palette.status = PaletteStatus::Started;
|
||||
palette.palette_type = palette_type.unwrap_or(PaletteType::File);
|
||||
palette.input = palette.palette_type.string();
|
||||
palette.input = input.unwrap_or_else(|| palette.palette_type.string());
|
||||
ctx.submit_command(Command::new(
|
||||
LAPCE_UI_COMMAND,
|
||||
LapceUICommand::InitPaletteInput(palette.input.clone()),
|
||||
|
@ -526,6 +555,9 @@ pub fn run(&mut self, ctx: &mut EventCtx, palette_type: Option<PaletteType>) {
|
|||
PaletteType::DocumentSymbol => {
|
||||
self.get_document_symbols(ctx);
|
||||
}
|
||||
PaletteType::WorkspaceSymbol => {
|
||||
self.get_workspace_symbols(ctx);
|
||||
}
|
||||
PaletteType::Workspace => {
|
||||
self.get_workspaces(ctx);
|
||||
}
|
||||
|
@ -570,6 +602,7 @@ pub fn delete_to_beginning_of_line(&mut self, ctx: &mut EventCtx) {
|
|||
PaletteType::SshHost => 0,
|
||||
PaletteType::Line => 1,
|
||||
PaletteType::DocumentSymbol => 1,
|
||||
PaletteType::WorkspaceSymbol => 1,
|
||||
PaletteType::Workspace => 1,
|
||||
PaletteType::Command => 1,
|
||||
PaletteType::GlobalSearch => 1,
|
||||
|
@ -643,7 +676,17 @@ pub fn select(&mut self, ctx: &mut EventCtx) {
|
|||
|
||||
pub fn update_input(&mut self, ctx: &mut EventCtx, input: String) {
|
||||
let palette = Arc::make_mut(&mut self.palette);
|
||||
|
||||
// WorkspaceSymbol requires sending the query to the lsp, so we refresh it when the input changes
|
||||
if input != palette.input
|
||||
&& matches!(palette.palette_type, PaletteType::WorkspaceSymbol)
|
||||
{
|
||||
self.run(ctx, Some(PaletteType::WorkspaceSymbol), Some(input));
|
||||
return;
|
||||
}
|
||||
|
||||
palette.input = input;
|
||||
|
||||
self.update_palette(ctx)
|
||||
}
|
||||
|
||||
|
@ -652,9 +695,10 @@ pub fn update_palette(&mut self, ctx: &mut EventCtx) {
|
|||
palette.index = 0;
|
||||
let palette_type = self.get_palette_type();
|
||||
if self.palette.palette_type != palette_type {
|
||||
self.run(ctx, Some(palette_type));
|
||||
self.run(ctx, Some(palette_type), None);
|
||||
return;
|
||||
}
|
||||
|
||||
if self.palette.get_input() != "" {
|
||||
let _ = self.palette.sender.send((
|
||||
self.palette.run_id.clone(),
|
||||
|
@ -679,6 +723,7 @@ fn get_palette_type(&self) -> PaletteType {
|
|||
match self.palette.input {
|
||||
_ if self.palette.input.starts_with('/') => PaletteType::Line,
|
||||
_ if self.palette.input.starts_with('@') => PaletteType::DocumentSymbol,
|
||||
_ if self.palette.input.starts_with('#') => PaletteType::WorkspaceSymbol,
|
||||
_ if self.palette.input.starts_with('>') => PaletteType::Workspace,
|
||||
_ if self.palette.input.starts_with(':') => PaletteType::Command,
|
||||
_ => PaletteType::File,
|
||||
|
@ -980,6 +1025,83 @@ fn get_document_symbols(&mut self, ctx: &mut EventCtx) {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_workspace_symbols(&mut self, ctx: &mut EventCtx) {
|
||||
let editor = self.main_split.active_editor();
|
||||
let editor = match editor {
|
||||
Some(editor) => editor,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let widget_id = self.palette.widget_id;
|
||||
|
||||
// TODO: We'd like to be able to request symbols even when not in an editor.
|
||||
if let BufferContent::File(path) = &editor.content {
|
||||
let buffer_id = self.main_split.open_docs.get(path).unwrap().id();
|
||||
let run_id = self.palette.run_id.clone();
|
||||
let event_sink = ctx.get_external_handle();
|
||||
|
||||
let query = self.palette.get_input();
|
||||
|
||||
self.palette.proxy.get_workspace_symbols(
|
||||
buffer_id,
|
||||
query,
|
||||
Box::new(move |result| {
|
||||
if let Ok(res) = result {
|
||||
let resp: Result<
|
||||
Option<Vec<SymbolInformation>>,
|
||||
serde_json::Error,
|
||||
> = serde_json::from_value(res);
|
||||
if let Ok(resp) = resp {
|
||||
let items: Vec<PaletteItem> = match resp {
|
||||
Some(symbols) => symbols
|
||||
.iter()
|
||||
.map(|s| {
|
||||
// TODO: Should we be using filter text?
|
||||
let mut filter_text = s.name.clone();
|
||||
if let Some(container_name) =
|
||||
s.container_name.as_ref()
|
||||
{
|
||||
filter_text += container_name;
|
||||
}
|
||||
PaletteItem {
|
||||
content:
|
||||
PaletteItemContent::WorkspaceSymbol {
|
||||
kind: s.kind,
|
||||
name: s.name.clone(),
|
||||
location: EditorLocation {
|
||||
path: path_from_url(
|
||||
&s.location.uri,
|
||||
),
|
||||
position: Some(
|
||||
s.location.range.start,
|
||||
),
|
||||
scroll_offset: None,
|
||||
history: None,
|
||||
},
|
||||
container_name: s
|
||||
.container_name
|
||||
.clone(),
|
||||
},
|
||||
filter_text,
|
||||
score: 0,
|
||||
indices: Vec::new(),
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
None => Vec::new(),
|
||||
};
|
||||
let _ = event_sink.submit_command(
|
||||
LAPCE_UI_COMMAND,
|
||||
LapceUICommand::UpdatePaletteItems(run_id, items),
|
||||
Target::Widget(widget_id),
|
||||
);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_process(
|
||||
receiver: Receiver<(String, String, Vec<PaletteItem>)>,
|
||||
widget_id: WidgetId,
|
||||
|
|
|
@ -661,6 +661,22 @@ pub fn get_document_symbols(&self, buffer_id: BufferId, f: Box<dyn Callback>) {
|
|||
);
|
||||
}
|
||||
|
||||
pub fn get_workspace_symbols(
|
||||
&self,
|
||||
buffer_id: BufferId,
|
||||
query: &str,
|
||||
f: Box<dyn Callback>,
|
||||
) {
|
||||
self.rpc.send_rpc_request_async(
|
||||
"get_workspace_symbols",
|
||||
&json!({
|
||||
"buffer_id": buffer_id,
|
||||
"query": query,
|
||||
}),
|
||||
f,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn get_code_actions(
|
||||
&self,
|
||||
buffer_id: BufferId,
|
||||
|
|
|
@ -533,6 +533,11 @@ fn handle_request(&self, id: RequestId, rpc: ProxyRequest) {
|
|||
let buffer = buffers.get(&buffer_id).unwrap();
|
||||
self.lsp.lock().get_document_symbols(id, buffer);
|
||||
}
|
||||
GetWorkspaceSymbols { query, buffer_id } => {
|
||||
let buffers = self.buffers.lock();
|
||||
let buffer = buffers.get(&buffer_id).unwrap();
|
||||
self.lsp.lock().get_workspace_symbols(id, buffer, query);
|
||||
}
|
||||
GetDocumentFormatting { buffer_id } => {
|
||||
let buffers = self.buffers.lock();
|
||||
let buffer = buffers.get(&buffer_id).unwrap();
|
||||
|
|
|
@ -191,6 +191,20 @@ pub fn get_document_symbols(&self, id: RequestId, buffer: &Buffer) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_workspace_symbols(
|
||||
&self,
|
||||
id: RequestId,
|
||||
buffer: &Buffer,
|
||||
query: String,
|
||||
) {
|
||||
// TODO: We could collate workspace symbols from all the lsps?
|
||||
if let Some(client) = self.clients.get(&buffer.language_id) {
|
||||
client.request_workspace_symbols(query, move |lsp_client, result| {
|
||||
lsp_client.dispatcher.respond(id, result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_document_formatting(&self, id: RequestId, buffer: &Buffer) {
|
||||
if let Some(client) = self.clients.get(&buffer.language_id) {
|
||||
let uri = client.get_uri(buffer);
|
||||
|
@ -790,6 +804,12 @@ pub fn send_initialize<CB>(&self, root_uri: Option<Url>, on_init: CB)
|
|||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
workspace: Some(WorkspaceClientCapabilities {
|
||||
symbol: Some(WorkspaceSymbolClientCapabilities {
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
experimental: Some(json!({
|
||||
"serverStatusNotification": true,
|
||||
})),
|
||||
|
@ -826,6 +846,18 @@ pub fn request_document_symbols<CB>(&self, document_uri: Url, cb: CB)
|
|||
self.send_request("textDocument/documentSymbol", params, Box::new(cb));
|
||||
}
|
||||
|
||||
pub fn request_workspace_symbols<CB>(&self, query: String, cb: CB)
|
||||
where
|
||||
CB: 'static + Send + FnOnce(&LspClient, Result<Value>),
|
||||
{
|
||||
let params = WorkspaceSymbolParams {
|
||||
query,
|
||||
..Default::default()
|
||||
};
|
||||
let params = Params::from(serde_json::to_value(params).unwrap());
|
||||
self.send_request("workspace/symbol", params, Box::new(cb));
|
||||
}
|
||||
|
||||
pub fn request_document_formatting<CB>(&self, document_uri: Url, cb: CB)
|
||||
where
|
||||
CB: 'static + Send + FnOnce(&LspClient, Result<Value>),
|
||||
|
|
|
@ -101,6 +101,12 @@ pub enum ProxyRequest {
|
|||
GetDocumentSymbols {
|
||||
buffer_id: BufferId,
|
||||
},
|
||||
GetWorkspaceSymbols {
|
||||
/// The search query
|
||||
query: String,
|
||||
/// THe id of the buffer it was used in, which tells us what LSP to query
|
||||
buffer_id: BufferId,
|
||||
},
|
||||
GetDocumentFormatting {
|
||||
buffer_id: BufferId,
|
||||
},
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
keypress::KeyPressFocus,
|
||||
palette::{PaletteStatus, PaletteType, PaletteViewData, PaletteViewLens},
|
||||
};
|
||||
use lsp_types::SymbolKind;
|
||||
|
||||
use crate::{
|
||||
editor::view::LapceEditorView,
|
||||
|
@ -102,7 +103,7 @@ fn event(
|
|||
LapceUICommand::RunPalette(palette_type) => {
|
||||
ctx.set_handled();
|
||||
let mut palette_data = data.palette_view_data();
|
||||
palette_data.run(ctx, palette_type.to_owned());
|
||||
palette_data.run(ctx, palette_type.to_owned(), None);
|
||||
data.palette = palette_data.palette.clone();
|
||||
data.keypress = palette_data.keypress.clone();
|
||||
data.workspace = palette_data.workspace.clone();
|
||||
|
@ -546,6 +547,7 @@ pub fn new() -> Self {
|
|||
fn paint_palette_item(
|
||||
palette_item_content: &PaletteItemContent,
|
||||
ctx: &mut PaintCtx,
|
||||
workspace_path: Option<&Path>,
|
||||
line: usize,
|
||||
indices: &[usize],
|
||||
line_height: f64,
|
||||
|
@ -589,6 +591,18 @@ fn paint_palette_item(
|
|||
.collect();
|
||||
(symbol_svg(kind), text, text_indices, hint, hint_indices)
|
||||
}
|
||||
PaletteItemContent::WorkspaceSymbol {
|
||||
kind,
|
||||
name,
|
||||
location,
|
||||
..
|
||||
} => Self::file_paint_symbols(
|
||||
&location.path,
|
||||
indices,
|
||||
workspace_path,
|
||||
name.as_str(),
|
||||
*kind,
|
||||
),
|
||||
PaletteItemContent::Line(_, text) => {
|
||||
(None, text.clone(), indices.to_vec(), "".to_string(), vec![])
|
||||
}
|
||||
|
@ -735,6 +749,47 @@ fn paint_palette_item(
|
|||
ctx.draw_text(&text_layout, point);
|
||||
}
|
||||
|
||||
fn file_paint_symbols(
|
||||
path: &Path,
|
||||
indices: &[usize],
|
||||
workspace_path: Option<&Path>,
|
||||
name: &str,
|
||||
kind: SymbolKind,
|
||||
) -> (Option<Svg>, String, Vec<usize>, String, Vec<usize>) {
|
||||
let text = name.to_string();
|
||||
let hint = path.to_string_lossy();
|
||||
// Remove the workspace prefix from the path
|
||||
let hint = workspace_path
|
||||
.and_then(Path::to_str)
|
||||
.and_then(|x| hint.strip_prefix(x))
|
||||
.map(|x| x.strip_prefix('/').unwrap_or(x))
|
||||
.map(ToString::to_string)
|
||||
.unwrap_or_else(|| hint.to_string());
|
||||
let text_indices = indices
|
||||
.iter()
|
||||
.filter_map(|i| {
|
||||
let i = *i;
|
||||
if i < text.len() {
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let hint_indices = indices
|
||||
.iter()
|
||||
.filter_map(|i| {
|
||||
let i = *i;
|
||||
if i >= text.len() {
|
||||
Some(i - text.len())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
(symbol_svg(&kind), text, text_indices, hint, hint_indices)
|
||||
}
|
||||
|
||||
fn file_paint_items(
|
||||
path: &Path,
|
||||
indices: &[usize],
|
||||
|
@ -856,6 +911,7 @@ fn paint(&mut self, ctx: &mut PaintCtx, data: &PaletteViewData, _env: &Env) {
|
|||
let start_line = (rect.y0 / self.line_height).floor() as usize;
|
||||
let end_line = (rect.y1 / self.line_height).ceil() as usize;
|
||||
|
||||
let workspace_path = data.workspace.path.as_deref();
|
||||
for line in start_line..end_line {
|
||||
if line >= items.len() {
|
||||
break;
|
||||
|
@ -874,6 +930,7 @@ fn paint(&mut self, ctx: &mut PaintCtx, data: &PaletteViewData, _env: &Env) {
|
|||
Self::paint_palette_item(
|
||||
&item.content,
|
||||
ctx,
|
||||
workspace_path,
|
||||
line,
|
||||
&item.indices,
|
||||
self.line_height,
|
||||
|
|
Loading…
Reference in New Issue