Skip to content

Commit

Permalink
Merge pull request #22 from koliyo/feature/incremental-change-buffer
Browse files Browse the repository at this point in the history
Incremental change events
  • Loading branch information
koliyo authored Oct 27, 2023
2 parents cd618a6 + 49caa24 commit f135ac1
Show file tree
Hide file tree
Showing 15 changed files with 452 additions and 235 deletions.
2 changes: 1 addition & 1 deletion JSONRPC-DataChannel-UniSocket
2 changes: 1 addition & 1 deletion LanguageClient
Submodule LanguageClient updated 1 files
+2 −2 Package.swift
2 changes: 1 addition & 1 deletion LanguageServerProtocol
13 changes: 11 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@
"version" : "0.1.1"
}
},
{
"identity" : "jsonrpc",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ChimeHQ/JSONRPC",
"state" : {
"revision" : "c6ec759d41a76ac88fe7327c41a77d9033943374",
"version" : "0.9.0"
}
},
{
"identity" : "processenv",
"kind" : "remoteSourceControl",
Expand Down Expand Up @@ -86,8 +95,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2",
"version" : "1.0.4"
"revision" : "a902f1823a7ff3c9ab2fba0f992396b948eda307",
"version" : "1.0.5"
}
},
{
Expand Down
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import PackageDescription
import Foundation

let commonCompileSetting: SwiftSetting =
.unsafeFlags(["-strict-concurrency=complete", "-warn-concurrency"])
.unsafeFlags([])
// .unsafeFlags(["-strict-concurrency=complete", "-warn-concurrency"])



Expand Down
38 changes: 31 additions & 7 deletions Sources/hylo-lsp-client/hylo-lsp-client-cli.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ struct Options: ParsableArguments {

#if os(Windows)
// let search1 = try Regex(#"(.+)(?::(\d+)(?:\.(\d+))?)"#)
logger.warning("Document path parsing not currently supported on Windows, assuming normal filepath")
print("Document path parsing not currently supported on Windows, assuming normal filepath")
let path = docLocation
let url = resolveDocumentUrl(path)
let uri = url.absoluteString
Expand Down Expand Up @@ -210,6 +210,28 @@ func resolveDocumentUri(_ uri: String) -> DocumentUri {
return resolveDocumentUrl(uri).absoluteString
}

func printDiagnostic(_ d: LanguageServerProtocol.Diagnostic, in filepath: String) {
print("\(cliLink(uri: filepath, range: d.range)) \(d.severity ?? .information): \(d.message)")
for ri in d.relatedInformation ?? [] {
print(" \(cliLink(uri: ri.location.uri, range: ri.location.range)) \(ri.message)")
}
}

func withDiagnosticsCheck<T>(_ fn: () async throws -> T) async throws -> T {
do {
return try await fn()
}
catch let d as DiagnosticSet {

for d in d.elements {
let _d = LanguageServerProtocol.Diagnostic(d)
printDiagnostic(_d, in: d.site.file.url.path)
}

throw d
}
}

protocol DocumentCommand : AsyncParsableCommand {
func process(doc: DocumentLocation, using server: Server) async throws
}
Expand Down Expand Up @@ -242,7 +264,6 @@ extension HyloLspCommand {

let response = try await server.documentSymbol(params: params)


switch response {
case nil:
print("No symbols")
Expand Down Expand Up @@ -348,10 +369,13 @@ extension HyloLspCommand {
func process(doc: DocumentLocation, using server: Server) async throws {
let params = DocumentDiagnosticParams(textDocument: TextDocumentIdentifier(uri: doc.uri))
let report = try await server.diagnostics(params: params)
for i in report.items ?? [] {
print("\(cliLink(uri: doc.filepath, range: i.range)) \(i.severity ?? .information): \(i.message)")
for ri in i.relatedInformation ?? [] {
print(" \(cliLink(uri: ri.location.uri, range: ri.location.range)) \(ri.message)")
for d in report.items ?? [] {
printDiagnostic(d, in: doc.filepath)
}

for (f, r) in report.relatedDocuments ?? [:] {
for d in r.items ?? [] {
printDiagnostic(d, in: f)
}
}
}
Expand Down Expand Up @@ -442,7 +466,7 @@ extension HyloLspCommand {
try socket.bind()
try socket.listen()
print("Created socket pipe: \(pipe)")
let client = try socket.accept()
_ = try socket.accept()
print("LSP attached")
// client.timeout = (connect: 5, read: nil, write: 5)
// let clientChannel = DataChannel(socket: client)
Expand Down
2 changes: 1 addition & 1 deletion Sources/hylo-lsp-server/hylo-lsp-server-cli.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ struct HyloLspCommand: AsyncParsableCommand {
}

func logHandlerFactory(_ label: String, fileLogger: FileLogger) -> LogHandler {
if ServerState.disableLogging {
if HyloServer.disableLogging {
return NullLogHandler(label: label)
}

Expand Down
10 changes: 8 additions & 2 deletions Sources/hylo-lsp/AST+StdLibrary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ import FrontEnd
import Foundation

extension AST {
internal init(libraryRoot: URL) throws {
internal init(sourceFiles: [SourceFile]) throws {
self.init()
var diagnostics = DiagnosticSet()
coreLibrary = try makeModule(
"Hylo",
sourceCode: sourceFiles(in: [libraryRoot]),
sourceCode: sourceFiles,
builtinModuleAccess: true,
diagnostics: &diagnostics)

assert(isCoreModuleLoaded)
}

internal init(libraryRoot: URL) throws {
let sourceFiles = try sourceFiles(in: [libraryRoot])
try self.init(sourceFiles: sourceFiles)
}
}
131 changes: 78 additions & 53 deletions Sources/hylo-lsp/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,91 +3,116 @@ import Core
import FrontEnd
import Foundation

public struct DocumentProfiling {
public let stdlibParsing: TimeInterval
public let ASTParsing: TimeInterval
public let typeChecking: TimeInterval
}

public struct AnalyzedDocument {
public struct Document {
public let uri: DocumentUri
public let program: TypedProgram
public let ast: AST
public let profiling: DocumentProfiling
public let version: Int?
public let text: String

public init(uri: DocumentUri, ast: AST, program: TypedProgram, profiling: DocumentProfiling) {
public init(uri: DocumentUri, version: Int?, text: String) {
self.uri = uri
self.ast = ast
self.program = program
self.profiling = profiling
self.version = version
self.text = text
}
}

// public struct CachedDocumentResult: Codable {
// public var uri: DocumentUri
// public var symbols: DocumentSymbolResponse?
// public var semanticTokens: SemanticTokensResponse?
// }
public init(textDocument: TextDocumentItem) {
uri = textDocument.uri
version = textDocument.version
text = textDocument.text
}
}

struct InvalidDocumentChangeRange : Error {
public let range: LSPRange
}

public actor DocumentContext {
public var uri: DocumentUri { request.uri }
public let request: DocumentBuildRequest
private var analyzedDocument: Result<AnalyzedDocument, Error>?
extension Document {

public init(_ request: DocumentBuildRequest) {
self.request = request
Task {
await self.monitorTasks()
public func withAppliedChanges(_ changes: [TextDocumentContentChangeEvent], nextVersion: Int?) throws -> Document {
var text = self.text
for c in changes {
try Document.applyChange(c, on: &text)
}
}

public func pollAnalyzedDocument() -> Result<AnalyzedDocument, Error>? {
return analyzedDocument
return Document(uri: uri, version: nextVersion, text: text)
}

public func getAnalyzedDocument() async -> Result<AnalyzedDocument, Error> {
do {
let doc = try await request.buildTask.value
return .success(doc)
}
catch {
return .failure(error)
private static func findPosition(_ position: Position, in text: String, startingFrom: String.Index) -> String.Index? {

var it = text[startingFrom...]
for _ in 0..<position.line {
guard let i = it.firstIndex(of: "\n") else {
return nil
}

it = it[it.index(after: i)...]
}

return text.index(it.startIndex, offsetBy: position.character)
}

public func getAST() async -> Result<AST, Error> {
do {
let ast = try await request.astTask.value
return .success(ast)
private static func findRange(_ range: LSPRange, in text: String) -> Range<String.Index>? {
guard let startIndex = findPosition(range.start, in: text, startingFrom: text.startIndex) else {
return nil
}
catch {
return .failure(error)

guard let endIndex = findPosition(range.end, in: text, startingFrom: startIndex) else {
return nil
}

return startIndex..<endIndex
}

private func monitorTasks() {
private static func applyChange(_ change: TextDocumentContentChangeEvent, on text: inout String) throws {
if let range = change.range {
guard let range = findRange(range, in: text) else {
throw InvalidDocumentChangeRange(range: range)
}

Task {
self.analyzedDocument = await getAnalyzedDocument()
text.replaceSubrange(range, with: change.text)
}
else {
text = change.text
}
}
}

public struct DocumentBuildRequest {
public struct DocumentProfiling {
public let stdlibParsing: TimeInterval
public let ASTParsing: TimeInterval
public let typeChecking: TimeInterval
}

public struct AnalyzedDocument {
public let uri: DocumentUri
public let astTask: Task<AST, Error>
public let buildTask: Task<AnalyzedDocument, Error>
public let program: TypedProgram
public let ast: AST
public let profiling: DocumentProfiling

public init(uri: DocumentUri, astTask: Task<AST, Error>, buildTask: Task<AnalyzedDocument, Error>) {
public init(uri: DocumentUri, ast: AST, program: TypedProgram, profiling: DocumentProfiling) {
self.uri = uri
self.astTask = astTask
self.buildTask = buildTask
self.ast = ast
self.program = program
self.profiling = profiling
}
}

extension DocumentProvider {
// This should really be a struct since we are building for Hylo
class DocumentContext {
public var doc: Document
public var uri: DocumentUri { doc.uri }
var astTask: Task<AST, Error>?
var buildTask: Task<AnalyzedDocument, Error>?

public init(_ doc: Document) {
self.doc = doc
}
}
}


public enum DocumentError : Error {
case diagnostics(DiagnosticSet)
case other(Error)
}

Loading

0 comments on commit f135ac1

Please sign in to comment.