forked from FuelLabs/sway
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enter tracking for comment continuation (FuelLabs#3979)
## Description Closes FuelLabs#3520 ![Feb-03-2023 14-20-39](https://user-images.githubusercontent.com/47993817/216722350-07bbcbb0-3f1f-4370-aa56-7dce5c22a341.gif) - Adds two new client config options: disable/enable continuation of doc comments (`///`) and of regular comments (`//`) - doc comment continuation is on by default, while regular comment continuation is off by default, since those are usually not multiline - Supports pasting multiline text into a comment block I implemented this using `did_change` rather than a custom LSP method or keybindings so that this feature will be available to all LSP-enabled editors without any additional setup. I didn't implement anything for closing curly braces since this is already supported in sway via the default `"editor.autoClosingBrackets": "languageDefined"` setting in VSCode. It will use the same indentation as the line before it, unless auto-indentation is turned off. ## Checklist - [x] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [ ] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [x] I have added tests that prove my fix is effective or that my feature works. - [x] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers. --------- Co-authored-by: Joshua Batty <[email protected]>
- Loading branch information
1 parent
ddd1b8b
commit c170dd1
Showing
7 changed files
with
259 additions
and
12 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
use crate::{ | ||
config::OnEnterConfig, | ||
core::{document::TextDocument, session::Session}, | ||
}; | ||
use std::sync::Arc; | ||
use tower_lsp::{ | ||
lsp_types::{ | ||
DidChangeTextDocumentParams, DocumentChanges, OneOf, | ||
OptionalVersionedTextDocumentIdentifier, Position, Range, TextDocumentEdit, TextEdit, Url, | ||
WorkspaceEdit, | ||
}, | ||
Client, | ||
}; | ||
|
||
const NEWLINE: &str = "\n"; | ||
const COMMENT_START: &str = "//"; | ||
const DOC_COMMENT_START: &str = "///"; | ||
|
||
/// If the change was an enter keypress or pasting multiple lines in a comment, it prefixes the line(s) | ||
/// with the appropriate comment start pattern (// or ///). | ||
pub(crate) async fn on_enter( | ||
config: &OnEnterConfig, | ||
client: &Client, | ||
session: &Arc<Session>, | ||
temp_uri: &Url, | ||
params: &DidChangeTextDocumentParams, | ||
) { | ||
if !(params.content_changes[0].text.contains(NEWLINE)) { | ||
return; | ||
} | ||
|
||
let mut workspace_edit = None; | ||
let text_document = session | ||
.get_text_document(temp_uri) | ||
.expect("could not get text document"); | ||
|
||
if config.continue_doc_comments.unwrap_or(false) { | ||
workspace_edit = get_comment_workspace_edit(DOC_COMMENT_START, params, &text_document); | ||
} | ||
|
||
if config.continue_comments.unwrap_or(false) && workspace_edit.is_none() { | ||
workspace_edit = get_comment_workspace_edit(COMMENT_START, params, &text_document); | ||
} | ||
|
||
// Apply any edits. | ||
if let Some(edit) = workspace_edit { | ||
if let Err(err) = client.apply_edit(edit).await { | ||
tracing::error!("on_enter failed to apply edit: {}", err); | ||
} | ||
} | ||
} | ||
|
||
fn get_comment_workspace_edit( | ||
start_pattern: &str, | ||
change_params: &DidChangeTextDocumentParams, | ||
text_document: &TextDocument, | ||
) -> Option<WorkspaceEdit> { | ||
let range = change_params.content_changes[0] | ||
.range | ||
.expect("change is missing range"); | ||
let line = text_document.get_line(range.start.line as usize); | ||
if line.trim().starts_with(start_pattern) { | ||
let uri = change_params.text_document.uri.clone(); | ||
let text = change_params.content_changes[0].text.clone(); | ||
|
||
let indentation = &line[..line.find(start_pattern).unwrap_or(0)]; | ||
let mut edits = vec![]; | ||
|
||
// To support pasting multiple lines in a comment, we need to add the comment start pattern after each newline, | ||
// except the last one. | ||
let lines: Vec<_> = text.split(NEWLINE).collect(); | ||
lines.iter().enumerate().for_each(|(i, _)| { | ||
if i < lines.len() - 1 { | ||
let position = | ||
Position::new(range.start.line + (i as u32) + 1, indentation.len() as u32); | ||
edits.push(OneOf::Left(TextEdit { | ||
new_text: format!("{start_pattern} "), | ||
range: Range::new(position, position), | ||
})); | ||
} | ||
}); | ||
let edit = TextDocumentEdit { | ||
text_document: OptionalVersionedTextDocumentIdentifier { | ||
// Use the original uri to make updates, not the temporary one from the session. | ||
uri, | ||
version: None, | ||
}, | ||
edits, | ||
}; | ||
Some(WorkspaceEdit { | ||
document_changes: Some(DocumentChanges::Edits(vec![edit])), | ||
..Default::default() | ||
}) | ||
} else { | ||
None | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::utils::test::get_absolute_path; | ||
use tower_lsp::lsp_types::{ | ||
AnnotatedTextEdit, TextDocumentContentChangeEvent, VersionedTextDocumentIdentifier, | ||
}; | ||
|
||
fn assert_text_edit( | ||
actual: &OneOf<TextEdit, AnnotatedTextEdit>, | ||
new_text: String, | ||
line: u32, | ||
character: u32, | ||
) { | ||
match actual { | ||
OneOf::Left(edit) => { | ||
let position = Position { line, character }; | ||
let expected = TextEdit { | ||
new_text, | ||
range: Range { | ||
start: position, | ||
end: position, | ||
}, | ||
}; | ||
assert_eq!(*edit, expected); | ||
} | ||
OneOf::Right(_) => panic!("expected left"), | ||
} | ||
} | ||
|
||
#[test] | ||
fn get_comment_workspace_edit_double_slash_indented() { | ||
let path = get_absolute_path("sway-lsp/test/fixtures/diagnostics/dead_code/src/main.sw"); | ||
let uri = Url::from_file_path(path.clone()).unwrap(); | ||
let text_document = | ||
TextDocument::build_from_path(path.as_str()).expect("failed to build document"); | ||
let params = DidChangeTextDocumentParams { | ||
text_document: VersionedTextDocumentIdentifier { uri, version: 1 }, | ||
content_changes: vec![TextDocumentContentChangeEvent { | ||
range: Some(Range { | ||
start: Position { | ||
line: 47, | ||
character: 34, | ||
}, | ||
end: Position { | ||
line: 47, | ||
character: 34, | ||
}, | ||
}), | ||
range_length: Some(0), | ||
text: "\n ".to_string(), | ||
}], | ||
}; | ||
|
||
let result = get_comment_workspace_edit(COMMENT_START, ¶ms, &text_document) | ||
.expect("workspace edit"); | ||
let changes = result.document_changes.expect("document changes"); | ||
let edits = match changes { | ||
DocumentChanges::Edits(edits) => edits, | ||
DocumentChanges::Operations(_) => panic!("expected edits"), | ||
}; | ||
|
||
assert_eq!(edits.len(), 1); | ||
assert_eq!(edits[0].edits.len(), 1); | ||
assert_text_edit(&edits[0].edits[0], "// ".to_string(), 48, 4); | ||
} | ||
|
||
#[test] | ||
fn get_comment_workspace_edit_triple_slash_paste() { | ||
let path = get_absolute_path("sway-lsp/test/fixtures/diagnostics/dead_code/src/main.sw"); | ||
let uri = Url::from_file_path(path.clone()).unwrap(); | ||
let text_document = | ||
TextDocument::build_from_path(path.as_str()).expect("failed to build document"); | ||
let params = DidChangeTextDocumentParams { | ||
text_document: VersionedTextDocumentIdentifier { uri, version: 1 }, | ||
content_changes: vec![TextDocumentContentChangeEvent { | ||
range: Some(Range { | ||
start: Position { | ||
line: 41, | ||
character: 4, | ||
}, | ||
end: Position { | ||
line: 41, | ||
character: 34, | ||
}, | ||
}), | ||
range_length: Some(30), | ||
text: "fn not_used2(input: u64) -> u64 {\n return input + 1;\n}".to_string(), | ||
}], | ||
}; | ||
|
||
let result = get_comment_workspace_edit(DOC_COMMENT_START, ¶ms, &text_document) | ||
.expect("workspace edit"); | ||
let changes = result.document_changes.expect("document changes"); | ||
let edits = match changes { | ||
DocumentChanges::Edits(edits) => edits, | ||
DocumentChanges::Operations(_) => panic!("expected edits"), | ||
}; | ||
|
||
assert_eq!(edits.len(), 1); | ||
assert_eq!(edits[0].edits.len(), 2); | ||
assert_text_edit(&edits[0].edits[0], "/// ".to_string(), 42, 0); | ||
assert_text_edit(&edits[0].edits[1], "/// ".to_string(), 43, 0); | ||
} | ||
} |
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