@ -19,7 +19,8 @@ use crate::{
ui ::{ self , overlay ::overlayed , FileLocation , FilePicker , Popup , PromptEvent } ,
ui ::{ self , overlay ::overlayed , FileLocation , FilePicker , Popup , PromptEvent } ,
} ;
} ;
use std ::{ borrow ::Cow , collections ::BTreeMap } ;
use std ::collections ::BTreeMap ;
use std ::{ borrow ::Cow , path ::PathBuf } ;
/// Gets the language server that is attached to a document, and
/// Gets the language server that is attached to a document, and
/// if it's not active displays a status message. Using this macro
/// if it's not active displays a status message. Using this macro
@ -39,6 +40,112 @@ macro_rules! language_server {
} ;
} ;
}
}
impl ui ::menu ::Item for lsp ::Location {
/// Current working directory.
type Data = PathBuf ;
fn label ( & self , cwdir : & Self ::Data ) -> Spans {
let file : Cow < ' _ , str > = ( self . uri . scheme ( ) = = "file" )
. then ( | | {
self . uri
. to_file_path ( )
. map ( | path | {
// strip root prefix
path . strip_prefix ( & cwdir )
. map ( | path | path . to_path_buf ( ) )
. unwrap_or ( path )
} )
. map ( | path | Cow ::from ( path . to_string_lossy ( ) . into_owned ( ) ) )
. ok ( )
} )
. flatten ( )
. unwrap_or_else ( | | self . uri . as_str ( ) . into ( ) ) ;
let line = self . range . start . line ;
format! ( "{}:{}" , file , line ) . into ( )
}
}
impl ui ::menu ::Item for lsp ::SymbolInformation {
/// Path to currently focussed document
type Data = Option < lsp ::Url > ;
fn label ( & self , current_doc_path : & Self ::Data ) -> Spans {
if current_doc_path . as_ref ( ) = = Some ( & self . location . uri ) {
self . name . as_str ( ) . into ( )
} else {
match self . location . uri . to_file_path ( ) {
Ok ( path ) = > {
let relative_path = helix_core ::path ::get_relative_path ( path . as_path ( ) )
. to_string_lossy ( )
. into_owned ( ) ;
format! ( "{} ({})" , & self . name , relative_path ) . into ( )
}
Err ( _ ) = > format! ( "{} ({})" , & self . name , & self . location . uri ) . into ( ) ,
}
}
}
}
struct DiagnosticStyles {
hint : Style ,
info : Style ,
warning : Style ,
error : Style ,
}
struct PickerDiagnostic {
url : lsp ::Url ,
diag : lsp ::Diagnostic ,
}
impl ui ::menu ::Item for PickerDiagnostic {
type Data = DiagnosticStyles ;
fn label ( & self , styles : & Self ::Data ) -> Spans {
let mut style = self
. diag
. severity
. map ( | s | match s {
DiagnosticSeverity ::HINT = > styles . hint ,
DiagnosticSeverity ::INFORMATION = > styles . info ,
DiagnosticSeverity ::WARNING = > styles . warning ,
DiagnosticSeverity ::ERROR = > styles . error ,
_ = > Style ::default ( ) ,
} )
. unwrap_or_default ( ) ;
// remove background as it is distracting in the picker list
style . bg = None ;
let code = self
. diag
. code
. as_ref ( )
. map ( | c | match c {
NumberOrString ::Number ( n ) = > n . to_string ( ) ,
NumberOrString ::String ( s ) = > s . to_string ( ) ,
} )
. unwrap_or_default ( ) ;
let truncated_path = path ::get_truncated_path ( self . url . path ( ) )
. to_string_lossy ( )
. into_owned ( ) ;
Spans ::from ( vec! [
Span ::styled (
self . diag . source . clone ( ) . unwrap_or_default ( ) ,
style . add_modifier ( Modifier ::BOLD ) ,
) ,
Span ::raw ( ": " ) ,
Span ::styled ( truncated_path , style ) ,
Span ::raw ( " - " ) ,
Span ::styled ( code , style . add_modifier ( Modifier ::BOLD ) ) ,
Span ::raw ( ": " ) ,
Span ::styled ( & self . diag . message , style ) ,
] )
}
}
fn location_to_file_location ( location : & lsp ::Location ) -> FileLocation {
fn location_to_file_location ( location : & lsp ::Location ) -> FileLocation {
let path = location . uri . to_file_path ( ) . unwrap ( ) ;
let path = location . uri . to_file_path ( ) . unwrap ( ) ;
let line = Some ( (
let line = Some ( (
@ -93,29 +200,14 @@ fn sym_picker(
offset_encoding : OffsetEncoding ,
offset_encoding : OffsetEncoding ,
) -> FilePicker < lsp ::SymbolInformation > {
) -> FilePicker < lsp ::SymbolInformation > {
// TODO: drop current_path comparison and instead use workspace: bool flag?
// TODO: drop current_path comparison and instead use workspace: bool flag?
let current_path2 = current_path . clone ( ) ;
FilePicker ::new (
FilePicker ::new (
symbols ,
symbols ,
move | symbol | {
current_path . clone ( ) ,
if current_path . as_ref ( ) = = Some ( & symbol . location . uri ) {
symbol . name . as_str ( ) . into ( )
} else {
match symbol . location . uri . to_file_path ( ) {
Ok ( path ) = > {
let relative_path = helix_core ::path ::get_relative_path ( path . as_path ( ) )
. to_string_lossy ( )
. into_owned ( ) ;
format! ( "{} ({})" , & symbol . name , relative_path ) . into ( )
}
Err ( _ ) = > format! ( "{} ({})" , & symbol . name , & symbol . location . uri ) . into ( ) ,
}
}
} ,
move | cx , symbol , action | {
move | cx , symbol , action | {
let ( view , doc ) = current ! ( cx . editor ) ;
let ( view , doc ) = current ! ( cx . editor ) ;
push_jump ( view , doc ) ;
push_jump ( view , doc ) ;
if current_path 2 . as_ref ( ) ! = Some ( & symbol . location . uri ) {
if current_path . as_ref ( ) ! = Some ( & symbol . location . uri ) {
let uri = & symbol . location . uri ;
let uri = & symbol . location . uri ;
let path = match uri . to_file_path ( ) {
let path = match uri . to_file_path ( ) {
Ok ( path ) = > path ,
Ok ( path ) = > path ,
@ -155,7 +247,7 @@ fn diag_picker(
diagnostics : BTreeMap < lsp ::Url , Vec < lsp ::Diagnostic > > ,
diagnostics : BTreeMap < lsp ::Url , Vec < lsp ::Diagnostic > > ,
current_path : Option < lsp ::Url > ,
current_path : Option < lsp ::Url > ,
offset_encoding : OffsetEncoding ,
offset_encoding : OffsetEncoding ,
) -> FilePicker < ( lsp ::Url , lsp ::Diagnostic ) > {
) -> FilePicker < PickerDiagnostic > {
// TODO: drop current_path comparison and instead use workspace: bool flag?
// TODO: drop current_path comparison and instead use workspace: bool flag?
// flatten the map to a vec of (url, diag) pairs
// flatten the map to a vec of (url, diag) pairs
@ -163,59 +255,24 @@ fn diag_picker(
for ( url , diags ) in diagnostics {
for ( url , diags ) in diagnostics {
flat_diag . reserve ( diags . len ( ) ) ;
flat_diag . reserve ( diags . len ( ) ) ;
for diag in diags {
for diag in diags {
flat_diag . push ( ( url . clone ( ) , diag ) ) ;
flat_diag . push ( PickerDiagnostic {
url : url . clone ( ) ,
diag ,
} ) ;
}
}
}
}
let hint = cx . editor . theme . get ( "hint" ) ;
let styles = DiagnosticStyles {
let info = cx . editor . theme . get ( "info" ) ;
hint : cx . editor . theme . get ( "hint" ) ,
let warning = cx . editor . theme . get ( "warning" ) ;
info : cx . editor . theme . get ( "info" ) ,
let error = cx . editor . theme . get ( "error" ) ;
warning : cx . editor . theme . get ( "warning" ) ,
error : cx . editor . theme . get ( "error" ) ,
} ;
FilePicker ::new (
FilePicker ::new (
flat_diag ,
flat_diag ,
move | ( url , diag ) | {
styles ,
let mut style = diag
move | cx , PickerDiagnostic { url , diag } , action | {
. severity
. map ( | s | match s {
DiagnosticSeverity ::HINT = > hint ,
DiagnosticSeverity ::INFORMATION = > info ,
DiagnosticSeverity ::WARNING = > warning ,
DiagnosticSeverity ::ERROR = > error ,
_ = > Style ::default ( ) ,
} )
. unwrap_or_default ( ) ;
// remove background as it is distracting in the picker list
style . bg = None ;
let code = diag
. code
. as_ref ( )
. map ( | c | match c {
NumberOrString ::Number ( n ) = > n . to_string ( ) ,
NumberOrString ::String ( s ) = > s . to_string ( ) ,
} )
. unwrap_or_default ( ) ;
let truncated_path = path ::get_truncated_path ( url . path ( ) )
. to_string_lossy ( )
. into_owned ( ) ;
Spans ::from ( vec! [
Span ::styled (
diag . source . clone ( ) . unwrap_or_default ( ) ,
style . add_modifier ( Modifier ::BOLD ) ,
) ,
Span ::raw ( ": " ) ,
Span ::styled ( truncated_path , style ) ,
Span ::raw ( " - " ) ,
Span ::styled ( code , style . add_modifier ( Modifier ::BOLD ) ) ,
Span ::raw ( ": " ) ,
Span ::styled ( & diag . message , style ) ,
] )
} ,
move | cx , ( url , diag ) , action | {
if current_path . as_ref ( ) = = Some ( url ) {
if current_path . as_ref ( ) = = Some ( url ) {
let ( view , doc ) = current ! ( cx . editor ) ;
let ( view , doc ) = current ! ( cx . editor ) ;
push_jump ( view , doc ) ;
push_jump ( view , doc ) ;
@ -233,7 +290,7 @@ fn diag_picker(
align_view ( doc , view , Align ::Center ) ;
align_view ( doc , view , Align ::Center ) ;
}
}
} ,
} ,
move | _editor , ( url , diag ) | {
move | _editor , PickerDiagnostic { url , diag } | {
let location = lsp ::Location ::new ( url . clone ( ) , diag . range ) ;
let location = lsp ::Location ::new ( url . clone ( ) , diag . range ) ;
Some ( location_to_file_location ( & location ) )
Some ( location_to_file_location ( & location ) )
} ,
} ,
@ -343,10 +400,11 @@ pub fn workspace_diagnostics_picker(cx: &mut Context) {
}
}
impl ui ::menu ::Item for lsp ::CodeActionOrCommand {
impl ui ::menu ::Item for lsp ::CodeActionOrCommand {
fn label ( & self ) -> & str {
type Data = ( ) ;
fn label ( & self , _data : & Self ::Data ) -> Spans {
match self {
match self {
lsp ::CodeActionOrCommand ::CodeAction ( action ) = > action . title . as_str ( ) ,
lsp ::CodeActionOrCommand ::CodeAction ( action ) = > action . title . as_str ( ) .into ( ) ,
lsp ::CodeActionOrCommand ::Command ( command ) = > command . title . as_str ( ) ,
lsp ::CodeActionOrCommand ::Command ( command ) = > command . title . as_str ( ) .into ( ) ,
}
}
}
}
}
}
@ -391,7 +449,7 @@ pub fn code_action(cx: &mut Context) {
return ;
return ;
}
}
let mut picker = ui ::Menu ::new ( actions , move | editor , code_action , event | {
let mut picker = ui ::Menu ::new ( actions , ( ) , move | editor , code_action , event | {
if event ! = PromptEvent ::Validate {
if event ! = PromptEvent ::Validate {
return ;
return ;
}
}
@ -619,6 +677,7 @@ pub fn apply_workspace_edit(
}
}
}
}
}
}
fn goto_impl (
fn goto_impl (
editor : & mut Editor ,
editor : & mut Editor ,
compositor : & mut Compositor ,
compositor : & mut Compositor ,
@ -637,26 +696,7 @@ fn goto_impl(
_locations = > {
_locations = > {
let picker = FilePicker ::new (
let picker = FilePicker ::new (
locations ,
locations ,
move | location | {
cwdir ,
let file : Cow < ' _ , str > = ( location . uri . scheme ( ) = = "file" )
. then ( | | {
location
. uri
. to_file_path ( )
. map ( | path | {
// strip root prefix
path . strip_prefix ( & cwdir )
. map ( | path | path . to_path_buf ( ) )
. unwrap_or ( path )
} )
. map ( | path | Cow ::from ( path . to_string_lossy ( ) . into_owned ( ) ) )
. ok ( )
} )
. flatten ( )
. unwrap_or_else ( | | location . uri . as_str ( ) . into ( ) ) ;
let line = location . range . start . line ;
format! ( "{}:{}" , file , line ) . into ( )
} ,
move | cx , location , action | {
move | cx , location , action | {
jump_to_location ( cx . editor , location , offset_encoding , action )
jump_to_location ( cx . editor , location , offset_encoding , action )
} ,
} ,