@@ -5,7 +5,8 @@ use std::process::{Command, Stdio};
55use anyhow:: Context ;
66
77use ruff_formatter:: { FormatOptions , PrintedRange } ;
8- use ruff_python_ast:: PySourceType ;
8+ use ruff_markdown:: { MarkdownResult , format_code_blocks} ;
9+ use ruff_python_ast:: { PySourceType , SourceType } ;
910use ruff_python_formatter:: { FormatModuleError , PyFormatOptions , format_module_source} ;
1011use ruff_source_file:: LineIndex ;
1112use ruff_text_size:: TextRange ;
@@ -30,7 +31,7 @@ pub(crate) enum FormatBackend {
3031
3132pub ( crate ) fn format (
3233 document : & TextDocument ,
33- source_type : PySourceType ,
34+ source_type : SourceType ,
3435 formatter_settings : & FormatterSettings ,
3536 path : & Path ,
3637 backend : FormatBackend ,
@@ -44,58 +45,103 @@ pub(crate) fn format(
4445/// Format using the built-in Ruff formatter.
4546fn format_internal (
4647 document : & TextDocument ,
47- source_type : PySourceType ,
48+ source_type : SourceType ,
4849 formatter_settings : & FormatterSettings ,
4950 path : & Path ,
5051) -> crate :: Result < Option < String > > {
51- let format_options =
52- formatter_settings. to_format_options ( source_type, document. contents ( ) , Some ( path) ) ;
53- match format_module_source ( document. contents ( ) , format_options) {
54- Ok ( formatted) => {
55- let formatted = formatted. into_code ( ) ;
56- if formatted == document. contents ( ) {
57- Ok ( None )
58- } else {
59- Ok ( Some ( formatted) )
52+ match source_type {
53+ SourceType :: Python ( py_source_type) => {
54+ let format_options = formatter_settings. to_format_options (
55+ py_source_type,
56+ document. contents ( ) ,
57+ Some ( path) ,
58+ ) ;
59+ match format_module_source ( document. contents ( ) , format_options) {
60+ Ok ( formatted) => {
61+ let formatted = formatted. into_code ( ) ;
62+ if formatted == document. contents ( ) {
63+ Ok ( None )
64+ } else {
65+ Ok ( Some ( formatted) )
66+ }
67+ }
68+ // Special case - syntax/parse errors are handled here instead of
69+ // being propagated as visible server errors.
70+ Err ( FormatModuleError :: ParseError ( error) ) => {
71+ tracing:: warn!( "Unable to format document: {error}" ) ;
72+ Ok ( None )
73+ }
74+ Err ( err) => Err ( err. into ( ) ) ,
6075 }
6176 }
62- // Special case - syntax/parse errors are handled here instead of
63- // being propagated as visible server errors.
64- Err ( FormatModuleError :: ParseError ( error) ) => {
65- tracing:: warn!( "Unable to format document: {error}" ) ;
77+ SourceType :: Markdown => {
78+ if !formatter_settings. preview . is_enabled ( ) {
79+ tracing:: warn!( "Markdown formatting is experimental, enable preview mode." ) ;
80+ return Ok ( None ) ;
81+ }
82+
83+ match format_code_blocks ( document. contents ( ) , Some ( path) , formatter_settings) {
84+ MarkdownResult :: Formatted ( formatted) => Ok ( Some ( formatted) ) ,
85+ MarkdownResult :: Unchanged => Ok ( None ) ,
86+ }
87+ }
88+ SourceType :: Toml ( _) => {
89+ tracing:: warn!( "Formatting TOML files not supported" ) ;
6690 Ok ( None )
6791 }
68- Err ( err) => Err ( err. into ( ) ) ,
6992 }
7093}
7194
7295/// Format using an external uv command.
7396fn format_external (
7497 document : & TextDocument ,
75- source_type : PySourceType ,
98+ source_type : SourceType ,
7699 formatter_settings : & FormatterSettings ,
77100 path : & Path ,
78101) -> crate :: Result < Option < String > > {
79- let format_options =
80- formatter_settings. to_format_options ( source_type, document. contents ( ) , Some ( path) ) ;
102+ let format_options = match source_type {
103+ SourceType :: Python ( py_source_type) => {
104+ formatter_settings. to_format_options ( py_source_type, document. contents ( ) , Some ( path) )
105+ }
106+ SourceType :: Markdown => formatter_settings. to_format_options (
107+ PySourceType :: Python ,
108+ document. contents ( ) ,
109+ Some ( path) ,
110+ ) ,
111+ SourceType :: Toml ( _) => {
112+ tracing:: warn!( "Formatting TOML files not supported" ) ;
113+ return Ok ( None ) ;
114+ }
115+ } ;
81116 let uv_command = UvFormatCommand :: from ( format_options) ;
82117 uv_command. format_document ( document. contents ( ) , path)
83118}
84119
85120pub ( crate ) fn format_range (
86121 document : & TextDocument ,
87- source_type : PySourceType ,
122+ source_type : SourceType ,
88123 formatter_settings : & FormatterSettings ,
89124 range : TextRange ,
90125 path : & Path ,
91126 backend : FormatBackend ,
92127) -> crate :: Result < Option < PrintedRange > > {
128+ let py_source_type = match source_type {
129+ SourceType :: Python ( py_source_type) => py_source_type,
130+ SourceType :: Markdown => {
131+ tracing:: warn!( "Range formatting for Markdown files not supported" ) ;
132+ return Ok ( None ) ;
133+ }
134+ SourceType :: Toml ( _) => {
135+ tracing:: warn!( "Formatting TOML files not supported" ) ;
136+ return Ok ( None ) ;
137+ }
138+ } ;
93139 match backend {
94140 FormatBackend :: Uv => {
95- format_range_external ( document, source_type , formatter_settings, range, path)
141+ format_range_external ( document, py_source_type , formatter_settings, range, path)
96142 }
97143 FormatBackend :: Internal => {
98- format_range_internal ( document, source_type , formatter_settings, range, path)
144+ format_range_internal ( document, py_source_type , formatter_settings, range, path)
99145 }
100146 }
101147}
@@ -327,7 +373,7 @@ mod tests {
327373
328374 use insta:: assert_snapshot;
329375 use ruff_linter:: settings:: types:: { CompiledPerFileTargetVersionList , PerFileTargetVersion } ;
330- use ruff_python_ast:: { PySourceType , PythonVersion } ;
376+ use ruff_python_ast:: { PySourceType , PythonVersion , SourceType } ;
331377 use ruff_text_size:: { TextRange , TextSize } ;
332378 use ruff_workspace:: FormatterSettings ;
333379
@@ -349,7 +395,7 @@ with open("a_really_long_foo") as foo, open("a_really_long_bar") as bar, open("a
349395 . unwrap ( ) ;
350396 let result = format (
351397 & document,
352- PySourceType :: Python ,
398+ SourceType :: Python ( PySourceType :: Python ) ,
353399 & FormatterSettings {
354400 unresolved_target_version : PythonVersion :: PY38 ,
355401 per_file_target_version,
@@ -373,7 +419,7 @@ with open("a_really_long_foo") as foo, open("a_really_long_bar") as bar, open("a
373419 // same as above but without the per_file_target_version override
374420 let result = format (
375421 & document,
376- PySourceType :: Python ,
422+ SourceType :: Python ( PySourceType :: Python ) ,
377423 & FormatterSettings {
378424 unresolved_target_version : PythonVersion :: PY38 ,
379425 ..Default :: default ( )
@@ -420,7 +466,7 @@ sys.exit(
420466 . unwrap ( ) ;
421467 let result = format_range (
422468 & document,
423- PySourceType :: Python ,
469+ SourceType :: Python ( PySourceType :: Python ) ,
424470 & FormatterSettings {
425471 unresolved_target_version : PythonVersion :: PY38 ,
426472 per_file_target_version,
@@ -445,7 +491,7 @@ sys.exit(
445491 // same as above but without the per_file_target_version override
446492 let result = format_range (
447493 & document,
448- PySourceType :: Python ,
494+ SourceType :: Python ( PySourceType :: Python ) ,
449495 & FormatterSettings {
450496 unresolved_target_version : PythonVersion :: PY38 ,
451497 ..Default :: default ( )
@@ -488,7 +534,7 @@ def world( ):
488534
489535 let result = format (
490536 & document,
491- PySourceType :: Python ,
537+ SourceType :: Python ( PySourceType :: Python ) ,
492538 & FormatterSettings :: default ( ) ,
493539 Path :: new ( "test.py" ) ,
494540 FormatBackend :: Uv ,
@@ -529,7 +575,7 @@ def another_function(x,y,z):
529575
530576 let result = format_range (
531577 & document,
532- PySourceType :: Python ,
578+ SourceType :: Python ( PySourceType :: Python ) ,
533579 & FormatterSettings :: default ( ) ,
534580 range,
535581 Path :: new ( "test.py" ) ,
@@ -571,7 +617,7 @@ def hello(very_long_parameter_name_1, very_long_parameter_name_2, very_long_para
571617
572618 let result = format (
573619 & document,
574- PySourceType :: Python ,
620+ SourceType :: Python ( PySourceType :: Python ) ,
575621 & formatter_settings,
576622 Path :: new ( "test.py" ) ,
577623 FormatBackend :: Uv ,
@@ -618,7 +664,7 @@ def hello():
618664
619665 let result = format (
620666 & document,
621- PySourceType :: Python ,
667+ SourceType :: Python ( PySourceType :: Python ) ,
622668 & formatter_settings,
623669 Path :: new ( "test.py" ) ,
624670 FormatBackend :: Uv ,
@@ -650,7 +696,7 @@ def broken(:
650696 // uv should return None for syntax errors (as indicated by the TODO comment)
651697 let result = format (
652698 & document,
653- PySourceType :: Python ,
699+ SourceType :: Python ( PySourceType :: Python ) ,
654700 & FormatterSettings :: default ( ) ,
655701 Path :: new ( "test.py" ) ,
656702 FormatBackend :: Uv ,
@@ -684,7 +730,7 @@ line'''
684730
685731 let result = format (
686732 & document,
687- PySourceType :: Python ,
733+ SourceType :: Python ( PySourceType :: Python ) ,
688734 & formatter_settings,
689735 Path :: new ( "test.py" ) ,
690736 FormatBackend :: Uv ,
@@ -726,7 +772,7 @@ bar = [1, 2, 3,]
726772
727773 let result = format (
728774 & document,
729- PySourceType :: Python ,
775+ SourceType :: Python ( PySourceType :: Python ) ,
730776 & formatter_settings,
731777 Path :: new ( "test.py" ) ,
732778 FormatBackend :: Uv ,
0 commit comments