diff --git a/ExtensionService/Assets.xcassets/CopilotLogo.imageset/Contents.json b/ExtensionService/Assets.xcassets/CopilotLogo.imageset/Contents.json new file mode 100644 index 0000000..2e35661 --- /dev/null +++ b/ExtensionService/Assets.xcassets/CopilotLogo.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "copilot.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/ExtensionService/Assets.xcassets/CopilotLogo.imageset/copilot.svg b/ExtensionService/Assets.xcassets/CopilotLogo.imageset/copilot.svg new file mode 100644 index 0000000..8284dce --- /dev/null +++ b/ExtensionService/Assets.xcassets/CopilotLogo.imageset/copilot.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotService.swift b/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotService.swift index 40a9f84..24dd5bf 100644 --- a/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotService.swift +++ b/Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotService.swift @@ -305,7 +305,7 @@ public final class GitHubCopilotService: GitHubCopilotBaseService, tabSize: tabSize, insertSpaces: !usesTabsForIndentation ), - context: .init(triggerKind: .invoked) + context: .init(triggerKind: .automatic) ))) .items .compactMap { (item: _) -> CodeSuggestion? in diff --git a/Tool/Sources/SharedUIComponents/AsyncCodeBlock.swift b/Tool/Sources/SharedUIComponents/AsyncCodeBlock.swift index 26c9711..f5bf5a1 100644 --- a/Tool/Sources/SharedUIComponents/AsyncCodeBlock.swift +++ b/Tool/Sources/SharedUIComponents/AsyncCodeBlock.swift @@ -102,7 +102,61 @@ public struct AsyncCodeBlock: View { return "Hold ⌥ for full suggestion" } - @ScaledMetric var ellipsisPadding: CGFloat = 5 + + @ScaledMetric var keyPadding: Double = 3.0 + + @ViewBuilder + func keyBackground(content: () -> some View) -> some View { + content() + .padding(.horizontal, keyPadding) + .background( + RoundedRectangle(cornerRadius: 2) + .stroke(foregroundColor, lineWidth: 1) + .foregroundColor(.clear) + .frame( + minWidth: fontHeight, + minHeight: fontHeight, + maxHeight: fontHeight + ) + ) + } + + @ViewBuilder + var optionKey: some View { + keyBackground { + Image(systemName: "option") + .resizable() + .renderingMode(.template) + .scaledToFit() + .frame(height: font.capHeight) + } + } + + @ViewBuilder + var popoverContent: some View { + HStack { + if isExpanded { + Text("Press") + optionKey + keyBackground { + Text("tab") + .font(.init(font)) + } + Text("to accept full suggestion") + } else { + Text("Hold") + optionKey + Text("for full suggestion") + } + } + .padding(8) + .font(.body) + .fixedSize() + } + + @ScaledMetric var iconPadding: CGFloat = 9.0 + @ScaledMetric var iconSpacing: CGFloat = 6.0 + @ScaledMetric var optionPadding: CGFloat = 0.5 @ViewBuilder var contentView: some View { @@ -116,30 +170,39 @@ public struct AsyncCodeBlock: View { .foregroundColor(foregroundTextColor) .lineSpacing(lineSpacing) // This only has effect if a line wraps if lines.count > 1 { - Image(systemName: "ellipsis") - .renderingMode(.template) - .foregroundColor(foregroundTextColor) - .padding(.horizontal, ellipsisPadding) - .background( - RoundedRectangle(cornerRadius: 12) - .fill(Color.gray.opacity(isExpanded ? 0.1 : 0.4)) - .frame(height: fontHeight * 0.75) - ) - .popover(isPresented: $isHovering) { - Text(hintText) - .font(.body) - .padding(8) - .fixedSize() - } - .task { - isHovering = !completionHintShown - completionHintShown = true - } + HStack(spacing: iconSpacing) { + Image("CopilotLogo") + .resizable() + .renderingMode(.template) + .scaledToFit() + Image(systemName: "option") + .resizable() + .renderingMode(.template) + .scaledToFit() + .padding(.vertical, optionPadding) + } + .frame(height: lineHeight * 0.7) + .padding(.horizontal, iconPadding) + .background( + Capsule() + .fill(foregroundColor.opacity(isExpanded ? 0.1 : 0.2)) + .frame(height: lineHeight) + ) + .frame(height: lineHeight) // Moves popover attachment + .popover(isPresented: $isHovering) { + popoverContent + } + .task { + isHovering = !completionHintShown + completionHintShown = true + } } } - .background(currentLineBackgroundColor ?? backgroundColor) + .frame(height: lineHeight) + .background( + HalfCapsule().fill(currentLineBackgroundColor ?? backgroundColor) + ) .padding(.leading, firstLineIndent) - .frame(minHeight: lineHeight) .onHover { hovering in guard hovering != isHovering else { return } withAnimation { diff --git a/Tool/Sources/SharedUIComponents/HalfCapsule.swift b/Tool/Sources/SharedUIComponents/HalfCapsule.swift new file mode 100644 index 0000000..68e9d0d --- /dev/null +++ b/Tool/Sources/SharedUIComponents/HalfCapsule.swift @@ -0,0 +1,19 @@ +import SwiftUI + +public struct HalfCapsule: Shape { + public func path(in rect: CGRect) -> Path { + Path { path in + path.move(to: .init(x:0, y: 0)) + path.addLine(to: .init(x:rect.width, y:0)) + path.addArc( + center: .init(x: rect.width - rect.height/2, y: rect.height/2), + radius: rect.height/2, + startAngle: .degrees(270), + endAngle: .degrees(90), + clockwise: false + ) + path.addLine(to: CGPoint(x:0, y:rect.height)) + path.addLine(to: CGPoint(x:0, y:rect.height)) + } + } +}