Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ struct InternalDevelopmentInspectorView: View {
var body: some View {
Form {
InternalDevelopmentNotificationsView()
InternalDevelopmentOutputView()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//
// InternalDevelopmentOutputView.swift
// CodeEdit
//
// Created by Khan Winter on 7/18/25.
//

import SwiftUI

struct InternalDevelopmentOutputView: View {
var body: some View {
Section("Output Utility") {
Button("Error Log") {
pushLog(.error)
}
Button("Warning Log") {
pushLog(.warning)
}
Button("Info Log") {
pushLog(.info)
}
Button("Debug Log") {
pushLog(.debug)
}
}

}

func pushLog(_ level: UtilityAreaLogLevel) {
InternalDevelopmentOutputSource.shared.pushLog(
.init(
message: randomString(),
subsystem: "internal.development",
category: "Logs",
level: level
)
)
}

func randomString() -> String {
let strings = ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce molestie, dui et consectetur"
+ "porttitor, orci lectus fermentum augue, eu faucibus lectus nisl id velit. Suspendisse in mi nunc. Aliquam"
+ "non dolor eu eros mollis euismod. Praesent mollis mauris at ex dapibus ornare. Ut imperdiet"
+ "finibus lacus ut aliquam. Vivamus semper, mauris in condimentum volutpat, quam erat eleifend ligula,"
+ "nec tincidunt sem ante et ex. Sed dui magna, placerat quis orci at, bibendum molestie massa. Maecenas"
+ "velit nunc, vehicula eu venenatis vel, tincidunt id purus. Morbi eu dignissim arcu, sed ornare odio."
+ "Nam vestibulum tempus nibh id finibus.").split(separator: " ")
let count = Int.random(in: 0..<25)
return (0..<count).compactMap { _ in
strings.randomElement()
}
.joined(separator: " ")
}
}
3 changes: 3 additions & 0 deletions CodeEdit/Features/LSP/LanguageServer/LanguageServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class LanguageServer<DocumentType: LanguageServerDocument> {
/// The configuration options this server supports.
var serverCapabilities: ServerCapabilities

var logContainer: LanguageServerLogContainer

/// An instance of a language server, that may or may not be initialized
private(set) var lspInstance: InitializingServer
/// The path to the root of the project
Expand All @@ -58,6 +60,7 @@ class LanguageServer<DocumentType: LanguageServerDocument> {
self.serverCapabilities = serverCapabilities
self.rootPath = rootPath
self.openFiles = LanguageServerFileMap()
self.logContainer = LanguageServerLogContainer(language: languageId)
self.logger = Logger(
subsystem: Bundle.main.bundleIdentifier ?? "",
category: "LanguageServer.\(languageId.rawValue)"
Expand Down
84 changes: 44 additions & 40 deletions CodeEdit/Features/LSP/Service/LSPService+Events.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// Created by Abe Malla on 6/1/24.
//

import Foundation
import LanguageClient
import LanguageServerProtocol

Expand Down Expand Up @@ -32,64 +33,67 @@ extension LSPService {
}

private func handleEvent(_ event: ServerEvent, for key: ClientKey) {
// TODO: Handle Events
// switch event {
// case let .request(id, request):
// print("Request ID: \(id) for \(key.languageId.rawValue)")
// handleRequest(request)
// case let .notification(notification):
// handleNotification(notification)
// case let .error(error):
// print("Error from EventStream for \(key.languageId.rawValue): \(error)")
// }
guard let client = languageClient(for: key.languageId, workspacePath: key.workspacePath) else {
return
}

switch event {
case let .request(_, request):
handleRequest(request, client: client)
case let .notification(notification):
handleNotification(notification, client: client)
case let .error(error):
logger.warning("Error from server \(key.languageId.rawValue, privacy: .public): \(error)")
}
}

private func handleRequest(_ request: ServerRequest) {
private func handleRequest(_ request: ServerRequest, client: LanguageServerType) {
// TODO: Handle Requests
// switch request {
// case let .workspaceConfiguration(params, _):
// print("workspaceConfiguration: \(params)")
// case let .workspaceFolders(handler):
// print("workspaceFolders: \(String(describing: handler))")
// case let .workspaceApplyEdit(params, _):
// print("workspaceApplyEdit: \(params)")
switch request {
// case let .workspaceConfiguration(params, _):
// print("workspaceConfiguration: \(params)")
// case let .workspaceFolders(handler):
// print("workspaceFolders: \(String(describing: handler))")
// case let .workspaceApplyEdit(params, _):
// print("workspaceApplyEdit: \(params)")
// case let .clientRegisterCapability(params, _):
// print("clientRegisterCapability: \(params)")
// case let .clientUnregisterCapability(params, _):
// print("clientUnregisterCapability: \(params)")
// case let .workspaceCodeLensRefresh(handler):
// print("workspaceCodeLensRefresh: \(String(describing: handler))")
// case let .workspaceCodeLensRefresh(handler):
// print("workspaceCodeLensRefresh: \(String(describing: handler))")
// case let .workspaceSemanticTokenRefresh(handler):
// print("workspaceSemanticTokenRefresh: \(String(describing: handler))")
// case let .windowShowMessageRequest(params, _):
// print("windowShowMessageRequest: \(params)")
// case let .windowShowDocument(params, _):
// print("windowShowDocument: \(params)")
// case let .windowWorkDoneProgressCreate(params, _):
// print("windowWorkDoneProgressCreate: \(params)")
//
// default:
// print()
// }
// print("Refresh semantic tokens!", handler)
// case let .windowShowMessageRequest(params, _):
// print("windowShowMessageRequest: \(params)")
// case let .windowShowDocument(params, _):
// print("windowShowDocument: \(params)")
// case let .windowWorkDoneProgressCreate(params, _):
// print("windowWorkDoneProgressCreate: \(params)")
default:
return
}
}

private func handleNotification(_ notification: ServerNotification) {
private func handleNotification(_ notification: ServerNotification, client: LanguageServerType) {
// TODO: Handle Notifications
// switch notification {
// case let .windowLogMessage(params):
// print("windowLogMessage \(params.type)\n```\n\(params.message)\n```\n")
switch notification {
case let .windowLogMessage(message):
client.logContainer.appendLog(message)
// case let .windowShowMessage(params):
// print("windowShowMessage \(params.type)\n```\n\(params.message)\n```\n")
// case let .textDocumentPublishDiagnostics(params):
// print("textDocumentPublishDiagnostics: \(params)")
// case let .textDocumentPublishDiagnostics(params):
// print("textDocumentPublishDiagnostics: \(params)")
// case let .telemetryEvent(params):
// print("telemetryEvent: \(params)")
// case let .protocolCancelRequest(params):
// print("protocolCancelRequest: \(params)")
// case let .protocolCancelRequest(params):
// print("protocolCancelRequest: \(params)")
// case let .protocolProgress(params):
// print("protocolProgress: \(params)")
// case let .protocolLogTrace(params):
// print("protocolLogTrace: \(params)")
// }
default:
return
}
}
}
4 changes: 2 additions & 2 deletions CodeEdit/Features/LSP/Service/LSPService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ final class LSPService: ObservableObject {
}

/// Holds the active language clients
var languageClients: [ClientKey: LanguageServerType] = [:]
@Published var languageClients: [ClientKey: LanguageServerType] = [:]
/// Holds the language server configurations for all the installed language servers
var languageConfigs: [LanguageIdentifier: LanguageServerBinary] = [:]
/// Holds all the event listeners for each active language client
Expand Down Expand Up @@ -212,7 +212,7 @@ final class LSPService: ObservableObject {
/// - Parameter document: The code document that was opened.
func openDocument(_ document: CodeFileDocument) {
guard let workspace = document.findWorkspace(),
let workspacePath = workspace.fileURL?.absoluteURL.path(),
let workspacePath = workspace.fileURL?.absolutePath,
let lspLanguage = document.getLanguage().lspLanguage else {
return
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// ExtensionUtilityAreaOutputSource.swift
// CodeEdit
//
// Created by Khan Winter on 7/18/25.
//

import OSLog
import LogStream

extension LogMessage: @retroactive Identifiable, UtilityAreaOutputMessage {
public var id: String {
"\(date.timeIntervalSince1970)" + process + (subsystem ?? "") + (category ?? "")
}

var level: UtilityAreaLogLevel {
switch type {
case .fault, .error:
.error
case .info, .default:
.info
case .debug:
.debug
default:
.info
}
}
}

struct ExtensionUtilityAreaOutputSource: UtilityAreaOutputSource {
var id: String {
"extension_output" + extensionInfo.id
}

let extensionInfo: ExtensionInfo

func cachedMessages() -> [LogMessage] {
[]
}

func streamMessages() -> AsyncStream<LogMessage> {
LogStream.logs(for: extensionInfo.pid, flags: [.info, .historical, .processOnly])
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// InternalDevelopmentOutputSource.swift
// CodeEdit
//
// Created by Khan Winter on 7/18/25.
//

import Foundation

class InternalDevelopmentOutputSource: UtilityAreaOutputSource {
static let shared = InternalDevelopmentOutputSource()

struct Message: UtilityAreaOutputMessage {
var id: UUID = UUID()

var message: String
var date: Date = Date()
var subsystem: String?
var category: String?
var level: UtilityAreaLogLevel
}

var id: UUID = UUID()
private var logs: [Message] = []
private(set) var streamContinuation: AsyncStream<Message>.Continuation
private var stream: AsyncStream<Message>

init() {
(stream, streamContinuation) = AsyncStream<Message>.makeStream()
}

func pushLog(_ log: Message) {
logs.append(log)
streamContinuation.yield(log)
}

func cachedMessages() -> [Message] {
logs
}

func streamMessages() -> AsyncStream<Message> {
stream
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// LanguageServerLogContainer.swift
// CodeEdit
//
// Created by Khan Winter on 7/18/25.
//

import OSLog
import LanguageServerProtocol

class LanguageServerLogContainer: UtilityAreaOutputSource {
struct LanguageServerMessage: UtilityAreaOutputMessage {
let log: LogMessageParams
var id: UUID = UUID()

var message: String {
log.message
}

var level: UtilityAreaLogLevel {
switch log.type {
case .error:
.error
case .warning:
.warning
case .info:
.info
case .log:
.debug
}
}

var date: Date = Date()
var subsystem: String?
var category: String?
}

let id: String

private var streamContinuation: AsyncStream<LanguageServerMessage>.Continuation
private var stream: AsyncStream<LanguageServerMessage>
private(set) var logs: [LanguageServerMessage] = []

init(language: LanguageIdentifier) {
id = language.rawValue
(stream, streamContinuation) = AsyncStream<LanguageServerMessage>.makeStream(
bufferingPolicy: .bufferingNewest(0)
)
}

func appendLog(_ log: LogMessageParams) {
let message = LanguageServerMessage(log: log)
logs.append(message)
streamContinuation.yield(message)
}

func cachedMessages() -> [LanguageServerMessage] {
logs
}

func streamMessages() -> AsyncStream<LanguageServerMessage> {
stream
}
}
Loading
Loading