66from subprocess import PIPE , Popen
77from typing import Dict , List
88
9- from lsprotocol .converters import get_converter
109from lsprotocol .types import (
1110 CodeAction ,
1211 CodeActionContext ,
2625
2726from pylsp_ruff .ruff import Check as RuffCheck
2827from pylsp_ruff .ruff import Fix as RuffFix
28+ from pylsp_ruff .settings import PluginSettings , get_converter
2929
3030log = logging .getLogger (__name__ )
3131converter = get_converter ()
5252def pylsp_settings ():
5353 log .debug ("Initializing pylsp_ruff" )
5454 # this plugin disables flake8, mccabe, and pycodestyle by default
55- return {
55+ settings = {
5656 "plugins" : {
57- "ruff" : {
58- "enabled" : True ,
59- "config" : None ,
60- "exclude" : None ,
61- "executable" : "ruff" ,
62- "ignore" : None ,
63- "extendIgnore" : None ,
64- "lineLength" : None ,
65- "perFileIgnores" : None ,
66- "select" : None ,
67- "extendSelect" : None ,
68- },
57+ "ruff" : PluginSettings (),
6958 "pyflakes" : {"enabled" : False },
7059 "flake8" : {"enabled" : False },
7160 "mccabe" : {"enabled" : False },
7261 "pycodestyle" : {"enabled" : False },
7362 }
7463 }
64+ return converter .unstructure (settings )
7565
7666
7767@hookimpl
@@ -342,9 +332,9 @@ def run_ruff(workspace: Workspace, document: Document, fix: bool = False) -> str
342332 -------
343333 String containing the result in json format.
344334 """
345- config = load_config (workspace , document )
346- executable = config . pop ( " executable" )
347- arguments = build_arguments (document , config , fix )
335+ settings = load_settings (workspace , document )
336+ executable = settings . executable
337+ arguments = build_arguments (document , settings , fix )
348338
349339 log .debug (f"Calling { executable } with args: { arguments } on '{ document .path } '" )
350340 try :
@@ -364,61 +354,74 @@ def run_ruff(workspace: Workspace, document: Document, fix: bool = False) -> str
364354 return stdout .decode ()
365355
366356
367- def build_arguments (document : Document , options : Dict , fix : bool = False ) -> List [str ]:
357+ def build_arguments (
358+ document : Document ,
359+ settings : PluginSettings ,
360+ fix : bool = False ,
361+ ) -> List [str ]:
368362 """
369363 Build arguments for ruff.
370364
371365 Parameters
372366 ----------
373367 document : pylsp.workspace.Document
374368 Document to apply ruff on.
375- options : Dict
376- Dict of arguments to pass to ruff.
369+ settings : PluginSettings
370+ Settings to use for arguments to pass to ruff.
377371
378372 Returns
379373 -------
380374 List containing the arguments.
381375 """
376+ args = []
382377 # Suppress update announcements
383- args = [ "--quiet" ]
378+ args . append ( "--quiet" )
384379 # Use the json formatting for easier evaluation
385- args .extend ([ "--format=json" ] )
380+ args .append ( "--format=json" )
386381 if fix :
387- args .extend ([ "--fix" ] )
382+ args .append ( "--fix" )
388383 else :
389384 # Do not attempt to fix -> returns file instead of diagnostics
390- args .extend ([ "--no-fix" ] )
385+ args .append ( "--no-fix" )
391386 # Always force excludes
392- args .extend ([ "--force-exclude" ] )
387+ args .append ( "--force-exclude" )
393388 # Pass filename to ruff for per-file-ignores, catch unsaved
394389 if document .path != "" :
395- args .extend (["--stdin-filename" , document .path ])
396-
397- # Convert per-file-ignores dict to right format
398- per_file_ignores = options .pop ("per-file-ignores" )
399-
400- if per_file_ignores :
401- for path , errors in per_file_ignores .items ():
402- errors = ("," ).join (errors )
403- if PurePath (document .path ).match (path ):
404- args .extend ([f"--ignore={ errors } " ])
405-
406- for arg_name , arg_val in options .items ():
407- if arg_val is None :
408- continue
409- arg = None
410- if isinstance (arg_val , list ):
411- arg = "--{}={}" .format (arg_name , "," .join (arg_val ))
412- else :
413- arg = "--{}={}" .format (arg_name , arg_val )
414- args .append (arg )
390+ args .append (f"--stdin-filename={ document .path } " )
391+
392+ if settings .config :
393+ args .append (f"--config={ settings .config } " )
394+
395+ if settings .line_length :
396+ args .append (f"--line-length={ settings .line_length } " )
397+
398+ if settings .exclude :
399+ args .append (f"--exclude={ ',' .join (settings .exclude )} " )
400+
401+ if settings .select :
402+ args .append (f"--select={ ',' .join (settings .select )} " )
403+
404+ if settings .extend_select :
405+ args .append (f"--extend-select={ ',' .join (settings .extend_select )} " )
406+
407+ if settings .ignore :
408+ args .append (f"--ignore={ ',' .join (settings .ignore )} " )
409+
410+ if settings .extend_ignore :
411+ args .append (f"--extend-ignore={ ',' .join (settings .extend_ignore )} " )
412+
413+ if settings .per_file_ignores :
414+ for path , errors in settings .per_file_ignores .items ():
415+ if not PurePath (document .path ).match (path ):
416+ continue
417+ args .append (f"--ignore={ ',' .join (errors )} " )
415418
416419 args .extend (["--" , "-" ])
417420
418421 return args
419422
420423
421- def load_config (workspace : Workspace , document : Document ) -> Dict :
424+ def load_settings (workspace : Workspace , document : Document ) -> PluginSettings :
422425 """
423426 Load settings from pyproject.toml file in the project path.
424427
@@ -431,10 +434,11 @@ def load_config(workspace: Workspace, document: Document) -> Dict:
431434
432435 Returns
433436 -------
434- Dictionary containing the settings to use when calling ruff .
437+ PluginSettings read via lsp .
435438 """
436439 config = workspace ._config
437- _settings = config .plugin_settings ("ruff" , document_path = document .path )
440+ _plugin_settings = config .plugin_settings ("ruff" , document_path = document .path )
441+ plugin_settings = converter .structure (_plugin_settings , PluginSettings )
438442
439443 pyproject_file = find_parents (
440444 workspace .root_path , document .path , ["pyproject.toml" ]
@@ -446,32 +450,12 @@ def load_config(workspace: Workspace, document: Document) -> Dict:
446450 f"Found pyproject file: { str (pyproject_file [0 ])} , "
447451 + "skipping pylsp config."
448452 )
449-
450453 # Leave config to pyproject.toml
451- settings = {
452- "config" : None ,
453- "exclude" : None ,
454- "executable" : _settings .get ("executable" , "ruff" ),
455- "ignore" : None ,
456- "extend-ignore" : _settings .get ("extendIgnore" , None ),
457- "line-length" : None ,
458- "per-file-ignores" : None ,
459- "select" : None ,
460- "extend-select" : _settings .get ("extendSelect" , None ),
461- }
462-
463- else :
464- # Default values are given by ruff
465- settings = {
466- "config" : _settings .get ("config" , None ),
467- "exclude" : _settings .get ("exclude" , None ),
468- "executable" : _settings .get ("executable" , "ruff" ),
469- "ignore" : _settings .get ("ignore" , None ),
470- "extend-ignore" : _settings .get ("extendIgnore" , None ),
471- "line-length" : _settings .get ("lineLength" , None ),
472- "per-file-ignores" : _settings .get ("perFileIgnores" , None ),
473- "select" : _settings .get ("select" , None ),
474- "extend-select" : _settings .get ("extendSelect" , None ),
475- }
454+ return PluginSettings (
455+ enabled = plugin_settings .executable ,
456+ executable = plugin_settings .executable ,
457+ extend_ignore = plugin_settings .extend_ignore ,
458+ extend_select = plugin_settings .extend_select ,
459+ )
476460
477- return settings
461+ return plugin_settings
0 commit comments