|
7 | 7 | from ....utils.logging import LoggingDescriptor |
8 | 8 | from ...common.language import language_id |
9 | 9 | from ...common.text_document import TextDocument |
10 | | -from ...common.types import FormattingOptions, Position, Range, TextEdit |
| 10 | +from ...common.types import FormattingOptions, MessageType, Position, Range, TextEdit |
11 | 11 |
|
12 | 12 | if TYPE_CHECKING: |
13 | 13 | from ..protocol import RobotLanguageServerProtocol |
14 | 14 |
|
| 15 | +from ..configuration import RoboTidyConfig |
15 | 16 | from .model_helper import ModelHelperMixin |
16 | 17 | from .protocol_part import RobotLanguageServerProtocolPart |
17 | 18 |
|
18 | 19 |
|
| 20 | +def robotidy_installed() -> bool: |
| 21 | + try: |
| 22 | + __import__("robotidy") |
| 23 | + except ImportError: |
| 24 | + return False |
| 25 | + return True |
| 26 | + |
| 27 | + |
19 | 28 | class RobotFormattingProtocolPart(RobotLanguageServerProtocolPart, ModelHelperMixin): |
20 | 29 | _logger = LoggingDescriptor() |
21 | 30 |
|
22 | 31 | def __init__(self, parent: RobotLanguageServerProtocol) -> None: |
23 | 32 | super().__init__(parent) |
24 | 33 |
|
25 | 34 | parent.formatting.format.add(self.format) |
26 | | - parent.formatting.format_range.add(self.format_range) |
| 35 | + # TODO implement range formatting |
| 36 | + # parent.formatting.format_range.add(self.format_range) |
27 | 37 |
|
28 | 38 | self.space_count = 4 |
29 | 39 | self.use_pipes = False |
30 | 40 | self.line_separator = os.linesep |
31 | 41 | self.short_test_name_length = 18 |
32 | 42 | self.setting_and_variable_name_length = 14 |
33 | 43 |
|
| 44 | + async def get_config(self, document: TextDocument) -> Optional[RoboTidyConfig]: |
| 45 | + folder = self.parent.workspace.get_workspace_folder(document.uri) |
| 46 | + if folder is None: |
| 47 | + return None |
| 48 | + |
| 49 | + return await self.parent.workspace.get_configuration(RoboTidyConfig, folder.uri) |
| 50 | + |
34 | 51 | @language_id("robotframework") |
35 | 52 | async def format( |
36 | 53 | self, sender: Any, document: TextDocument, options: FormattingOptions, **further_options: Any |
37 | 54 | ) -> Optional[List[TextEdit]]: |
| 55 | + config = await self.get_config(document) |
| 56 | + if config and config.enabled and robotidy_installed(): |
| 57 | + return await self.format_robot_tidy(document, options, **further_options) |
| 58 | + return await self.format_internal(document, options, **further_options) |
| 59 | + |
| 60 | + async def format_robot_tidy( |
| 61 | + self, document: TextDocument, options: FormattingOptions, **further_options: Any |
| 62 | + ) -> Optional[List[TextEdit]]: |
| 63 | + |
| 64 | + from difflib import SequenceMatcher |
| 65 | + |
| 66 | + from robotidy.api import RobotidyAPI |
| 67 | + |
| 68 | + try: |
| 69 | + model = await self.parent.documents_cache.get_model(document) |
| 70 | + |
| 71 | + robot_tidy = RobotidyAPI(document.uri.to_path(), None) |
| 72 | + |
| 73 | + changed, _, new = robot_tidy.transform(model) |
| 74 | + |
| 75 | + if not changed: |
| 76 | + return None |
| 77 | + |
| 78 | + new_lines = new.text.splitlines() |
| 79 | + |
| 80 | + result: List[TextEdit] = [] |
| 81 | + matcher = SequenceMatcher(a=document.lines, b=new_lines, autojunk=False) |
| 82 | + for code, old_start, old_end, new_start, new_end in matcher.get_opcodes(): |
| 83 | + if code == "insert" or code == "replace": |
| 84 | + result.append( |
| 85 | + TextEdit( |
| 86 | + range=Range( |
| 87 | + start=Position(line=old_start, character=0), |
| 88 | + end=Position(line=old_end, character=0), |
| 89 | + ), |
| 90 | + new_text=os.linesep.join(new_lines[new_start:new_end]) + os.linesep, |
| 91 | + ) |
| 92 | + ) |
| 93 | + |
| 94 | + elif code == "delete": |
| 95 | + result.append( |
| 96 | + TextEdit( |
| 97 | + range=Range( |
| 98 | + start=Position(line=old_start, character=0), |
| 99 | + end=Position(line=old_end, character=0), |
| 100 | + ), |
| 101 | + new_text="", |
| 102 | + ) |
| 103 | + ) |
| 104 | + |
| 105 | + if result: |
| 106 | + return result |
| 107 | + |
| 108 | + except BaseException as e: |
| 109 | + self.parent.window.show_message(str(e), MessageType.Error) |
| 110 | + return None |
| 111 | + |
| 112 | + async def format_internal( |
| 113 | + self, document: TextDocument, options: FormattingOptions, **further_options: Any |
| 114 | + ) -> Optional[List[TextEdit]]: |
38 | 115 |
|
39 | 116 | from robot.parsing.model.blocks import File |
40 | 117 | from robot.tidypkg import ( |
@@ -68,4 +145,8 @@ async def format( |
68 | 145 | async def format_range( |
69 | 146 | self, sender: Any, document: TextDocument, range: Range, options: FormattingOptions, **further_options: Any |
70 | 147 | ) -> Optional[List[TextEdit]]: |
| 148 | + # TODO implement range formatting |
| 149 | + # config = await self.get_config(document) |
| 150 | + # if config and config.enabled and robotidy_installed(): |
| 151 | + # return await self.format_robot_tidy(document, options, range=range, **further_options) |
71 | 152 | return None |
0 commit comments