diff --git a/Tool/Sources/CodeiumService/LanguageServer/CodeiumInstallationManager.swift b/Tool/Sources/CodeiumService/LanguageServer/CodeiumInstallationManager.swift index e2b25492..7f2c8866 100644 --- a/Tool/Sources/CodeiumService/LanguageServer/CodeiumInstallationManager.swift +++ b/Tool/Sources/CodeiumService/LanguageServer/CodeiumInstallationManager.swift @@ -3,7 +3,7 @@ import Terminal public struct CodeiumInstallationManager { private static var isInstalling = false - static let latestSupportedVersion = "1.8.8" + static let latestSupportedVersion = "1.8.83" public init() {} diff --git a/Tool/Sources/SuggestionProvider/PostProcessingSuggestionServiceMiddleware.swift b/Tool/Sources/SuggestionProvider/PostProcessingSuggestionServiceMiddleware.swift index e69e29d2..345dc1ef 100644 --- a/Tool/Sources/SuggestionProvider/PostProcessingSuggestionServiceMiddleware.swift +++ b/Tool/Sources/SuggestionProvider/PostProcessingSuggestionServiceMiddleware.swift @@ -15,6 +15,7 @@ public struct PostProcessingSuggestionServiceMiddleware: SuggestionServiceMiddle var suggestion = $0 if suggestion.text.allSatisfy({ $0.isWhitespace || $0.isNewline }) { return nil } Self.removeTrailingWhitespacesAndNewlines(&suggestion) + Self.removeRedundantClosingParenthesis(&suggestion, lines: request.lines) if !Self.checkIfSuggestionHasNoEffect(suggestion, request: request) { return nil } return suggestion } @@ -28,6 +29,44 @@ public struct PostProcessingSuggestionServiceMiddleware: SuggestionServiceMiddle suggestion.text = String(text) } + /// Remove the parenthesis in the last line of the suggestion if + /// - It contains only closing parenthesis + /// - It's identical to the next line below the range of the suggestion + static func removeRedundantClosingParenthesis( + _ suggestion: inout CodeSuggestion, + lines: [String] + ) { + let nextLineIndex = suggestion.range.end.line + 1 + guard nextLineIndex < lines.endIndex, nextLineIndex >= 0 else { return } + let nextLine = lines[nextLineIndex].dropLast(1) + let lineBreakIndex = suggestion.text.lastIndex(where: { $0.isNewline }) + let lastLineIndex = if let index = lineBreakIndex { + suggestion.text.index(after: index) + } else { + suggestion.text.startIndex + } + guard lastLineIndex < suggestion.text.endIndex else { return } + let lastSuggestionLine = suggestion.text[lastLineIndex...] + guard lastSuggestionLine == nextLine else { return } + + let closingParenthesis: [Character] = [")", "]", "}", ">"] + let validCharacters = Set(closingParenthesis + [" ", ","]) + + let trimmedLastSuggestionLine = nextLine.trimmingCharacters(in: .whitespacesAndNewlines) + guard !trimmedLastSuggestionLine.isEmpty else { return } + + if trimmedLastSuggestionLine == "```" + || trimmedLastSuggestionLine == "\"\"\"" + || trimmedLastSuggestionLine.allSatisfy({ validCharacters.contains($0) }) + { + if let lastIndex = lineBreakIndex { + suggestion.text = String(suggestion.text[.. 60 * 3 } - public private(set) var lastUpdateTime: Date = Environment.now() + public internal(set) var lastUpdateTime: Date = Environment.now() private var additionalProperties = FilespacePropertyValues() let fileSaveWatcher: FileSaveWatcher let onClose: (URL) -> Void diff --git a/Tool/Tests/SuggestionProviderTests/PostProcessingSuggestionServiceMiddlewareTests.swift b/Tool/Tests/SuggestionProviderTests/PostProcessingSuggestionServiceMiddlewareTests.swift index 378bfcf5..3ed29251 100644 --- a/Tool/Tests/SuggestionProviderTests/PostProcessingSuggestionServiceMiddlewareTests.swift +++ b/Tool/Tests/SuggestionProviderTests/PostProcessingSuggestionServiceMiddlewareTests.swift @@ -34,6 +34,33 @@ class PostProcessingSuggestionServiceMiddlewareTests: XCTestCase { relevantCodeSnippets: [] ) } + + func test_empty() async throws { + let middleware = PostProcessingSuggestionServiceMiddleware() + + let handler: PostProcessingSuggestionServiceMiddleware.Next = { _ in + [ + .init( + id: "1", + text: "", + position: .init(line: 0, character: 0), + range: .init(startPair: (0, 0), endPair: (0, 0)) + ), + ] + } + + let suggestions = try await middleware.getSuggestion( + createRequest("", .init(line: 0, character: 0)), + configuration: .init( + acceptsRelevantCodeSnippets: true, + mixRelevantCodeSnippetsInSource: true, + acceptsRelevantSnippetsFromOpenedFiles: true + ), + next: handler + ) + + XCTAssertEqual(suggestions, []) + } func test_trailing_whitespaces_and_new_lines_should_be_removed() async throws { let middleware = PostProcessingSuggestionServiceMiddleware() @@ -184,5 +211,175 @@ class PostProcessingSuggestionServiceMiddlewareTests: XCTestCase { ), ]) } + + func test_remove_duplicated_trailing_closing_parenthesis_single_parenthesis() async throws { + let middleware = PostProcessingSuggestionServiceMiddleware() + + let handler: PostProcessingSuggestionServiceMiddleware.Next = { _ in + [ + .init( + id: "1", + text: "hello world\n}", + position: .init(line: 0, character: 1), + range: .init(startPair: (0, 0), endPair: (0, 1)) + ), + ] + } + + let suggestions = try await middleware.getSuggestion( + createRequest("h\n}\n", .init(line: 0, character: 1)), + configuration: .init( + acceptsRelevantCodeSnippets: true, + mixRelevantCodeSnippetsInSource: true, + acceptsRelevantSnippetsFromOpenedFiles: true + ), + next: handler + ) + + XCTAssertEqual(suggestions, [ + .init( + id: "1", + text: "hello world", + position: .init(line: 0, character: 1), + range: .init(startPair: (0, 0), endPair: (0, 1)) + ), + ]) + } + + func test_remove_duplicated_trailing_closing_parenthesis_single_line() async throws { + let middleware = PostProcessingSuggestionServiceMiddleware() + + let handler: PostProcessingSuggestionServiceMiddleware.Next = { _ in + [ + .init( + id: "1", + text: "}", + position: .init(line: 0, character: 0), + range: .init(startPair: (0, 0), endPair: (0, 0)) + ), + ] + } + + let suggestions = try await middleware.getSuggestion( + createRequest("\n}\n", .init(line: 0, character: 0)), + configuration: .init( + acceptsRelevantCodeSnippets: true, + mixRelevantCodeSnippetsInSource: true, + acceptsRelevantSnippetsFromOpenedFiles: true + ), + next: handler + ) + + XCTAssertEqual(suggestions, [ + .init( + id: "1", + text: "", + position: .init(line: 0, character: 0), + range: .init(startPair: (0, 0), endPair: (0, 0)) + ), + ]) + } + + func test_remove_duplicated_trailing_closing_parenthesis_leading_space() async throws { + let middleware = PostProcessingSuggestionServiceMiddleware() + + let handler: PostProcessingSuggestionServiceMiddleware.Next = { _ in + [ + .init( + id: "1", + text: "hello world\n }", + position: .init(line: 0, character: 1), + range: .init(startPair: (0, 0), endPair: (0, 1)) + ), + ] + } + + let suggestions = try await middleware.getSuggestion( + createRequest("h\n }\n", .init(line: 0, character: 1)), + configuration: .init( + acceptsRelevantCodeSnippets: true, + mixRelevantCodeSnippetsInSource: true, + acceptsRelevantSnippetsFromOpenedFiles: true + ), + next: handler + ) + + XCTAssertEqual(suggestions, [ + .init( + id: "1", + text: "hello world", + position: .init(line: 0, character: 1), + range: .init(startPair: (0, 0), endPair: (0, 1)) + ), + ]) + } + + func test_remove_duplicated_trailing_closing_parenthesis_commas() async throws { + let middleware = PostProcessingSuggestionServiceMiddleware() + + let handler: PostProcessingSuggestionServiceMiddleware.Next = { _ in + [ + .init( + id: "1", + text: "hello world\n,},", + position: .init(line: 0, character: 1), + range: .init(startPair: (0, 0), endPair: (0, 1)) + ), + ] + } + + let suggestions = try await middleware.getSuggestion( + createRequest("h\n,},\n", .init(line: 0, character: 1)), + configuration: .init( + acceptsRelevantCodeSnippets: true, + mixRelevantCodeSnippetsInSource: true, + acceptsRelevantSnippetsFromOpenedFiles: true + ), + next: handler + ) + + XCTAssertEqual(suggestions, [ + .init( + id: "1", + text: "hello world", + position: .init(line: 0, character: 1), + range: .init(startPair: (0, 0), endPair: (0, 1)) + ), + ]) + } + + func test_remove_duplicated_trailing_closing_parenthesis_multiple_parenthesis() async throws { + let middleware = PostProcessingSuggestionServiceMiddleware() + + let handler: PostProcessingSuggestionServiceMiddleware.Next = { _ in + [ + .init( + id: "1", + text: "hello world\n}))>}}", + position: .init(line: 0, character: 1), + range: .init(startPair: (0, 0), endPair: (0, 1)) + ), + ] + } + + let suggestions = try await middleware.getSuggestion( + createRequest("h\n}))>}}\n", .init(line: 0, character: 1)), + configuration: .init( + acceptsRelevantCodeSnippets: true, + mixRelevantCodeSnippetsInSource: true, + acceptsRelevantSnippetsFromOpenedFiles: true + ), + next: handler + ) + + XCTAssertEqual(suggestions, [ + .init( + id: "1", + text: "hello world", + position: .init(line: 0, character: 1), + range: .init(startPair: (0, 0), endPair: (0, 1)) + ), + ]) + } } diff --git a/Version.xcconfig b/Version.xcconfig index 4c9957e3..ce865d72 100644 --- a/Version.xcconfig +++ b/Version.xcconfig @@ -1,3 +1,3 @@ -APP_VERSION = 0.33.5 -APP_BUILD = 394 +APP_VERSION = 0.33.7 +APP_BUILD = 398