Coverage for src/debputy/lsp/text_util.py: 67%

59 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-04-07 12:14 +0200

1from typing import List, Optional, Sequence, Union, Iterable 

2 

3from lsprotocol.types import ( 

4 TextEdit, 

5 Position, 

6 Range, 

7 WillSaveTextDocumentParams, 

8) 

9 

10from debputy.linting.lint_util import LinterPositionCodec 

11 

12try: 

13 from debian._deb822_repro.locatable import Position as TEPosition, Range as TERange 

14except ImportError: 

15 pass 

16 

17try: 

18 from pygls.workspace import LanguageServer, TextDocument, PositionCodec 

19 

20 LintCapablePositionCodec = Union[LinterPositionCodec, PositionCodec] 

21except ImportError: 

22 LintCapablePositionCodec = LinterPositionCodec 

23 

24 

25try: 

26 from Levenshtein import distance 

27except ImportError: 

28 

29 def detect_possible_typo( 

30 provided_value: str, 

31 known_values: Iterable[str], 

32 ) -> Sequence[str]: 

33 return tuple() 

34 

35else: 

36 

37 def detect_possible_typo( 

38 provided_value: str, 

39 known_values: Iterable[str], 

40 ) -> Sequence[str]: 

41 k_len = len(provided_value) 

42 candidates = [] 

43 for known_value in known_values: 

44 if abs(k_len - len(known_value)) > 2: 

45 continue 

46 d = distance(provided_value, known_value) 

47 if d > 2: 

48 continue 

49 candidates.append(known_value) 

50 return candidates 

51 

52 

53def normalize_dctrl_field_name(f: str) -> str: 

54 if not f or not f.startswith(("x", "X")): 

55 return f 

56 i = 0 

57 for i in range(1, len(f)): 57 ↛ 63line 57 didn't jump to line 63, because the loop on line 57 didn't complete

58 if f[i] == "-": 

59 i += 1 

60 break 

61 if f[i] not in ("b", "B", "s", "S", "c", "C"): 61 ↛ 62line 61 didn't jump to line 62, because the condition on line 61 was never true

62 return f 

63 assert i > 0 

64 return f[i:] 

65 

66 

67def on_save_trim_end_of_line_whitespace( 

68 ls: "LanguageServer", 

69 params: WillSaveTextDocumentParams, 

70) -> Optional[Sequence[TextEdit]]: 

71 doc = ls.workspace.get_text_document(params.text_document.uri) 

72 return trim_end_of_line_whitespace(doc, doc.lines) 

73 

74 

75def trim_end_of_line_whitespace( 

76 doc: "TextDocument", 

77 lines: List[str], 

78) -> Optional[Sequence[TextEdit]]: 

79 edits = [] 

80 for line_no, orig_line in enumerate(lines): 

81 orig_len = len(orig_line) 

82 if orig_line.endswith("\n"): 

83 orig_len -= 1 

84 stripped_len = len(orig_line.rstrip()) 

85 if stripped_len == orig_len: 

86 continue 

87 

88 edit_range = doc.position_codec.range_to_client_units( 

89 lines, 

90 Range( 

91 Position( 

92 line_no, 

93 stripped_len, 

94 ), 

95 Position( 

96 line_no, 

97 orig_len, 

98 ), 

99 ), 

100 ) 

101 edits.append( 

102 TextEdit( 

103 edit_range, 

104 "", 

105 ) 

106 ) 

107 

108 return edits 

109 

110 

111def te_position_to_lsp(te_position: "TEPosition") -> Position: 

112 return Position( 

113 te_position.line_position, 

114 te_position.cursor_position, 

115 ) 

116 

117 

118def te_range_to_lsp(te_range: "TERange") -> Range: 

119 return Range( 

120 te_position_to_lsp(te_range.start_pos), 

121 te_position_to_lsp(te_range.end_pos), 

122 )