Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 52 additions & 1 deletion ls/editors/code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,58 @@
"contributes": {
"configuration": {
"title": "YARA",
"properties": {}
"properties": {
"YARA.codeFormatting": {
"type": "object",
"default": {
"alignMetadata": true,
"alignPatterns": true,
"indentSectionHeaders": true,
"indentSectionContents": true,
"newlineBeforeCurlyBrace": false,
"emptyLineBeforeSectionHeader": false,
"emptyLineAfterSectionHeader": false
},
"description": "Options for automatic code formatting.",
"properties": {
"alignMetadata": {
"type": "boolean",
"default": true,
"markdownDescription": "Aligns the values in the `meta` section of a rule.\n\n**Example:**\n\n*Original code:*\n```yara\nrule example {\n meta:\n author = \"John Doe\"\n creation_date = \"2024-01-01\"\n description = \"A simple example rule.\"\n}\n```\n\n*Formatted code:*\n```yara\nrule example {\n meta:\n author = \"John Doe\"\n creation_date = \"2024-01-01\"\n description = \"A simple example rule.\"\n}\n```"
},
"alignPatterns": {
"type": "boolean",
"default": true,
"markdownDescription": "Aligns the patterns in the `strings` section of a rule.\n\n**Example:**\n\n*Original code:*\n```yara\nrule example {\n strings:\n $a = \"some string\"\n $b_is_longer = { 48 65 6c 6c 6f }\n $c = \"another string\"\n}\n```\n\n*Formatted code:*\n```yara\nrule example {\n strings:\n $a = \"some string\"\n $b_is_longer = { 48 65 6c 6c 6f }\n $c = \"another string\"\n}\n```"
},
"indentSectionHeaders": {
"type": "boolean",
"default": true,
"markdownDescription": "Indent section headers (`meta`, `strings`, `condition`).\n\n**Example:**\n\n*Original code:*\n```yara\nrule example {\nmeta:\n author = \"John Doe\"\nstrings:\n $a = \"some string\"\ncondition:\n $a\n}\n```\n\n*Formatted code:*\n```yara\nrule example {\n meta:\n author = \"John Doe\"\n strings:\n $a = \"some string\"\n condition:\n $a\n}\n```"
},
"indentSectionContents": {
"type": "boolean",
"default": true,
"markdownDescription": "Indent the content of rule sections.\n\n**Example:**\n\n*Original code:*\n```yara\nrule example {\n meta:\n author = \"John Doe\"\n strings:\n $a = \"some string\"\n condition:\n $a\n}\n```\n\n*Formatted code:*\n```yara\nrule example {\n meta:\n author = \"John Doe\"\n strings:\n $a = \"some string\"\n condition:\n $a\n}\n```"
},
"emptyLineBeforeSectionHeader": {
"type": "boolean",
"default": false,
"description": "Inserts an empty line before each section header."
},
"emptyLineAfterSectionHeader": {
"type": "boolean",
"default": false,
"description": "Inserts an empty line after each section header."
},
"newlineBeforeCurlyBrace": {
"type": "boolean",
"default": false,
"description": "Puts the opening curly brace on a new line."
}
}
}
}
},
"configurationDefaults": {
"[yara]": {
Expand Down
42 changes: 41 additions & 1 deletion ls/src/features/formatting.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use serde::Deserialize;
use std::{io::Cursor, sync::Arc};

use async_lsp::lsp_types::{
Expand All @@ -7,9 +8,36 @@ use yara_x_fmt::Indentation;

use crate::documents::storage::DocumentStorage;

#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct FormattingOptions {
pub align_metadata: bool,
pub align_patterns: bool,
pub indent_section_headers: bool,
pub indent_section_contents: bool,
pub newline_before_curly_brace: bool,
pub empty_line_before_section_header: bool,
pub empty_line_after_section_header: bool,
}

impl Default for FormattingOptions {
fn default() -> Self {
Self {
align_metadata: true,
align_patterns: true,
indent_section_headers: true,
indent_section_contents: true,
newline_before_curly_brace: false,
empty_line_before_section_header: false,
empty_line_after_section_header: false,
}
}
}

pub fn formatting(
documents: Arc<DocumentStorage>,
params: DocumentFormattingParams,
options: FormattingOptions,
) -> Option<Vec<TextEdit>> {
let document = documents.get(&params.text_document.uri)?;
let src = document.text.as_str();
Expand All @@ -23,7 +51,19 @@ pub fn formatting(
Indentation::Tabs
};

let formatter = yara_x_fmt::Formatter::new().indentation(indentation);
let formatter = yara_x_fmt::Formatter::new()
.indentation(indentation)
.align_metadata(options.align_metadata)
.align_patterns(options.align_patterns)
.indent_section_headers(options.indent_section_headers)
.indent_section_contents(options.indent_section_contents)
.newline_before_curly_brace(options.newline_before_curly_brace)
.empty_line_before_section_header(
options.empty_line_before_section_header,
)
.empty_line_after_section_header(
options.empty_line_after_section_header,
);

match formatter.format(input, &mut output) {
Ok(changed) if changed => Some(vec![TextEdit::new(
Expand Down
76 changes: 52 additions & 24 deletions ls/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ use async_lsp::lsp_types::request::{
use async_lsp::lsp_types::{
CodeActionParams, CodeActionProviderCapability, CodeActionResponse,
CompletionOptions, CompletionParams, CompletionResponse,
DiagnosticOptions, DiagnosticServerCapabilities,
DidChangeTextDocumentParams, DidCloseTextDocumentParams,
DidOpenTextDocumentParams, DidSaveTextDocumentParams,
DocumentDiagnosticParams, DocumentDiagnosticReportResult,
DocumentFormattingParams, DocumentHighlight, DocumentHighlightParams,
DocumentSymbolParams, DocumentSymbolResponse,
FullDocumentDiagnosticReport, GotoDefinitionParams,
GotoDefinitionResponse, Hover, HoverParams, HoverProviderCapability,
InitializeParams, InitializeResult, Location, OneOf,
PublishDiagnosticsParams, ReferenceParams,
ConfigurationItem, ConfigurationParams, DiagnosticOptions,
DiagnosticServerCapabilities, DidChangeTextDocumentParams,
DidCloseTextDocumentParams, DidOpenTextDocumentParams,
DidSaveTextDocumentParams, DocumentDiagnosticParams,
DocumentDiagnosticReportResult, DocumentFormattingParams,
DocumentHighlight, DocumentHighlightParams, DocumentSymbolParams,
DocumentSymbolResponse, FullDocumentDiagnosticReport,
GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverParams,
HoverProviderCapability, InitializeParams, InitializeResult, Location,
OneOf, PublishDiagnosticsParams, ReferenceParams,
RelatedFullDocumentDiagnosticReport, RenameParams, SaveOptions,
SelectionRange, SelectionRangeParams, SelectionRangeProviderCapability,
SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions,
Expand All @@ -37,12 +37,14 @@ use async_lsp::router::Router;
use async_lsp::{ClientSocket, LanguageClient, LanguageServer, ResponseError};
use futures::future::BoxFuture;

use crate::documents::storage::DocumentStorage;
use crate::features::code_action::code_actions;
use crate::features::completion::completion;
use crate::features::diagnostics::diagnostics;
use crate::features::document_highlight::document_highlight;
use crate::features::document_symbol::document_symbol;
use crate::features::formatting::formatting;
use crate::features::formatting::FormattingOptions;
use crate::features::goto::go_to_definition;
use crate::features::hover::hover;
use crate::features::references::find_references;
Expand All @@ -52,8 +54,6 @@ use crate::features::semantic_tokens::{
semantic_tokens, SEMANTIC_TOKEN_MODIFIERS, SEMANTIC_TOKEN_TYPES,
};

use crate::documents::storage::DocumentStorage;

/// Represents a YARA language server.
pub struct YARALanguageServer {
/// Client socket for communication with the Development Tool.
Expand Down Expand Up @@ -123,7 +123,9 @@ impl LanguageServer for YARALanguageServer {
range: Some(true),
legend: SemanticTokensLegend {
token_types: Vec::from(SEMANTIC_TOKEN_TYPES),
token_modifiers: Vec::from(SEMANTIC_TOKEN_MODIFIERS),
token_modifiers: Vec::from(
SEMANTIC_TOKEN_MODIFIERS,
),
},
..Default::default()
},
Expand All @@ -147,22 +149,30 @@ impl LanguageServer for YARALanguageServer {
document_highlight_provider: Some(OneOf::Left(true)),
document_symbol_provider: Some(OneOf::Left(true)),
rename_provider: Some(OneOf::Left(true)),
code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
code_action_provider: Some(
CodeActionProviderCapability::Simple(true),
),
selection_range_provider: Some(
SelectionRangeProviderCapability::Simple(true),
),
text_document_sync: Some(TextDocumentSyncCapability::Options(
TextDocumentSyncOptions {
save: Some(TextDocumentSyncSaveOptions::SaveOptions(SaveOptions {
include_text: Some(true),
})),
save: Some(TextDocumentSyncSaveOptions::SaveOptions(
SaveOptions {
include_text: Some(true),
},
)),
open_close: Some(true),
change: Some(TextDocumentSyncKind::FULL),
..Default::default()
},
)),
// This is for pull model diagnostics
diagnostic_provider: Some(DiagnosticServerCapabilities::Options(
DiagnosticOptions::default(),
)),
diagnostic_provider: Some(
DiagnosticServerCapabilities::Options(
DiagnosticOptions::default(),
),
),
..ServerCapabilities::default()
},
server_info: None,
Expand Down Expand Up @@ -415,17 +425,35 @@ impl LanguageServer for YARALanguageServer {
})
}

/// This method is called when the user requests to format a document.
///
/// It formats the source code according to the configured style and
/// returns a set of edits to apply the changes.
fn formatting(
&mut self,
params: DocumentFormattingParams,
) -> BoxFuture<'static, Result<Option<Vec<TextEdit>>, Self::Error>> {
let documents = Arc::clone(&self.documents);
let mut client = self.client.clone();

Box::pin(async move { Ok(formatting(documents, params)) })
Box::pin(async move {
let config = client
.configuration(ConfigurationParams {
items: vec![ConfigurationItem {
scope_uri: Some(params.text_document.uri.clone()),
section: Some("YARA.codeFormatting".to_string()),
}],
})
.await;

let options = config
.ok()
.and_then(|mut config| config.pop())
.and_then(|v| {
serde_json::from_value::<FormattingOptions>(v).ok()
})
.unwrap_or_default();

Ok(formatting(documents, params, options))
})
}

/// This method is called when a document is opened.
Expand Down
2 changes: 1 addition & 1 deletion ls/src/tests/testdata/formatting1.response.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"newText": "rule test {\n strings:\n $a = \"some string\"\n\n condition:\n $a\n}\n",
"newText": "rule test {\n strings:\n $a = \"some string\"\n condition:\n $a\n}\n",
"range": {
"end": {
"character": 0,
Expand Down
Loading