diff --git a/projects/compiler/src/parser.abra b/projects/compiler/src/parser.abra index 0642599b..0791cb63 100644 --- a/projects/compiler/src/parser.abra +++ b/projects/compiler/src/parser.abra @@ -1700,10 +1700,11 @@ export type Parser { _ => false } val invokee = if isSome { + val bogusPosition = Position(line: 0, col: 0) AstNode( token: token, kind: AstNodeKind.Accessor(AccessorAstNode( - root: AstNode(token: Token(position: token.position, kind: TokenKind.Ident("Option")), kind: AstNodeKind.Identifier(IdentifierKind.Named("Option"))), + root: AstNode(token: Token(position: bogusPosition, kind: TokenKind.Ident("Option")), kind: AstNodeKind.Identifier(IdentifierKind.Named("Option"))), path: [(Token(position: token.position, kind: TokenKind.Dot), Label(name: "Some", position: token.position))] )) ) diff --git a/projects/compiler/src/typechecker.abra b/projects/compiler/src/typechecker.abra index e66f14b8..f91ae8ef 100644 --- a/projects/compiler/src/typechecker.abra +++ b/projects/compiler/src/typechecker.abra @@ -95,10 +95,16 @@ export enum IdentifierKindMeta { Type(isEnum: Bool, typeParams: String[]) } +export enum IdentifierMetaImport { + Prelude + Module(filePath: String) +} + export type IdentifierMeta { name: String kind: IdentifierKindMeta - importedFrom: String? = None + importedFrom: IdentifierMetaImport? = None + definitionPosition: Position? = None } export type TypedModule { @@ -3058,6 +3064,7 @@ export type Typechecker { val ident = IdentifierMeta( name: label.name, kind: IdentifierKindMeta.Variable(mutable: mutable, typeRepr: ty.repr()), + definitionPosition: Some(label.position), ) val v = (label.position.col - 1, label.position.col + label.name.length - 1, ident) @@ -4023,10 +4030,11 @@ export type Typechecker { } IdentifierKind.Discard => return Err(TypeError(position: token.position, kind: TypeErrorKind.UnknownName("_", "variable"))) IdentifierKind.None_ => { + val bogusPosition = Position(line: 0, col: 0) val replacement = AstNode( token: token, kind: AstNodeKind.Accessor(AccessorAstNode( - root: AstNode(token: Token(position: token.position, kind: TokenKind.Ident("Option")), kind: AstNodeKind.Identifier(IdentifierKind.Named("Option"))), + root: AstNode(token: Token(position: bogusPosition, kind: TokenKind.Ident("Option")), kind: AstNodeKind.Identifier(IdentifierKind.Named("Option"))), path: [(Token(position: token.position, kind: TokenKind.Dot), Label(name: "None", position: token.position))] )) ) @@ -4067,10 +4075,19 @@ export type Typechecker { VariableAlias.Enum(_enum) => IdentifierKindMeta.Type(isEnum: true, typeParams: _enum.typeParams) } + val importedFrom = if varImportMod |mod| { + Some(IdentifierMetaImport.Module(mod.name)) + } else if variable.scope == self.project.preludeScope { + Some(IdentifierMetaImport.Prelude) + } else { + None + } + val ident = IdentifierMeta( name: name, kind: kind, - importedFrom: varImportMod?.name, + importedFrom: importedFrom, + definitionPosition: Some(variable.label.position), ) val v = (token.position.col - 1, token.position.col + name.length - 1, ident) diff --git a/projects/compiler/test/parser/invocation_transform_OptionSome.out.json b/projects/compiler/test/parser/invocation_transform_OptionSome.out.json index 917f9bea..92bb1fe1 100644 --- a/projects/compiler/test/parser/invocation_transform_OptionSome.out.json +++ b/projects/compiler/test/parser/invocation_transform_OptionSome.out.json @@ -37,7 +37,7 @@ "name": "accessor", "root": { "token": { - "position": [1, 13], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/array/array.out.json b/projects/compiler/test/typechecker/array/array.out.json index 08659ede..3f5e7aa0 100644 --- a/projects/compiler/test/typechecker/array/array.out.json +++ b/projects/compiler/test/typechecker/array/array.out.json @@ -512,7 +512,7 @@ "kind": "accessor", "head": { "token": { - "position": [7, 28], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -666,7 +666,7 @@ "kind": "accessor", "head": { "token": { - "position": [8, 19], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -1069,7 +1069,7 @@ "kind": "accessor", "head": { "token": { - "position": [11, 20], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -1368,7 +1368,7 @@ "kind": "accessor", "head": { "token": { - "position": [12, 30], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -1605,7 +1605,7 @@ "kind": "accessor", "head": { "token": { - "position": [14, 28], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -2076,7 +2076,7 @@ "kind": "accessor", "head": { "token": { - "position": [18, 13], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/binary/eq.2.out.json b/projects/compiler/test/typechecker/binary/eq.2.out.json index b5092646..c2bd0f04 100644 --- a/projects/compiler/test/typechecker/binary/eq.2.out.json +++ b/projects/compiler/test/typechecker/binary/eq.2.out.json @@ -136,7 +136,7 @@ "kind": "accessor", "head": { "token": { - "position": [2, 6], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/binary/neq.2.out.json b/projects/compiler/test/typechecker/binary/neq.2.out.json index 29cba25e..d8984f8b 100644 --- a/projects/compiler/test/typechecker/binary/neq.2.out.json +++ b/projects/compiler/test/typechecker/binary/neq.2.out.json @@ -136,7 +136,7 @@ "kind": "accessor", "head": { "token": { - "position": [2, 6], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/bindingdecl/bindingdecl.out.json b/projects/compiler/test/typechecker/bindingdecl/bindingdecl.out.json index 08042aa0..ad74f2e8 100644 --- a/projects/compiler/test/typechecker/bindingdecl/bindingdecl.out.json +++ b/projects/compiler/test/typechecker/bindingdecl/bindingdecl.out.json @@ -240,7 +240,7 @@ "kind": "accessor", "head": { "token": { - "position": [8, 16], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/funcdecl/funcdecl.5.out.json b/projects/compiler/test/typechecker/funcdecl/funcdecl.5.out.json index daf2b638..a523e636 100644 --- a/projects/compiler/test/typechecker/funcdecl/funcdecl.5.out.json +++ b/projects/compiler/test/typechecker/funcdecl/funcdecl.5.out.json @@ -230,7 +230,7 @@ "kind": "accessor", "head": { "token": { - "position": [3, 49], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -372,7 +372,7 @@ "kind": "accessor", "head": { "token": { - "position": [4, 31], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -586,7 +586,7 @@ "kind": "accessor", "head": { "token": { - "position": [5, 32], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -812,7 +812,7 @@ "kind": "accessor", "head": { "token": { - "position": [6, 32], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -916,7 +916,7 @@ "kind": "accessor", "head": { "token": { - "position": [6, 48], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/identifier/identifier.out.json b/projects/compiler/test/typechecker/identifier/identifier.out.json index 63600532..0569e0d3 100644 --- a/projects/compiler/test/typechecker/identifier/identifier.out.json +++ b/projects/compiler/test/typechecker/identifier/identifier.out.json @@ -164,7 +164,7 @@ "kind": "accessor", "head": { "token": { - "position": [4, 15], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/identifier/identifier_transform_OptionNone.out.json b/projects/compiler/test/typechecker/identifier/identifier_transform_OptionNone.out.json index 20905307..5bd23318 100644 --- a/projects/compiler/test/typechecker/identifier/identifier_transform_OptionNone.out.json +++ b/projects/compiler/test/typechecker/identifier/identifier_transform_OptionNone.out.json @@ -56,7 +56,7 @@ "kind": "accessor", "head": { "token": { - "position": [1, 15], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/if/expr.out.json b/projects/compiler/test/typechecker/if/expr.out.json index 0dedbc79..6b664bc8 100644 --- a/projects/compiler/test/typechecker/if/expr.out.json +++ b/projects/compiler/test/typechecker/if/expr.out.json @@ -750,7 +750,7 @@ "kind": "accessor", "head": { "token": { - "position": [21, 3], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -1194,7 +1194,7 @@ "kind": "accessor", "head": { "token": { - "position": [32, 3], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -1427,7 +1427,7 @@ "kind": "accessor", "head": { "token": { - "position": [36, 38], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -1605,7 +1605,7 @@ "kind": "accessor", "head": { "token": { - "position": [39, 19], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -1954,7 +1954,7 @@ "kind": "accessor", "head": { "token": { - "position": [45, 3], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -2127,7 +2127,7 @@ "kind": "accessor", "head": { "token": { - "position": [49, 16], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/import/import_type_identifier.1.out.json b/projects/compiler/test/typechecker/import/import_type_identifier.1.out.json index 58c443fa..684fb1ac 100644 --- a/projects/compiler/test/typechecker/import/import_type_identifier.1.out.json +++ b/projects/compiler/test/typechecker/import/import_type_identifier.1.out.json @@ -615,7 +615,7 @@ "kind": "accessor", "head": { "token": { - "position": [3, 15], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -695,7 +695,7 @@ "kind": "accessor", "head": { "token": { - "position": [4, 17], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/import/import_type_identifier.2.out.json b/projects/compiler/test/typechecker/import/import_type_identifier.2.out.json index 1fed10ef..a7e8e602 100644 --- a/projects/compiler/test/typechecker/import/import_type_identifier.2.out.json +++ b/projects/compiler/test/typechecker/import/import_type_identifier.2.out.json @@ -615,7 +615,7 @@ "kind": "accessor", "head": { "token": { - "position": [3, 17], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/invocation/invocation.5.out.json b/projects/compiler/test/typechecker/invocation/invocation.5.out.json index 8a78eb43..ceca7280 100644 --- a/projects/compiler/test/typechecker/invocation/invocation.5.out.json +++ b/projects/compiler/test/typechecker/invocation/invocation.5.out.json @@ -342,7 +342,7 @@ "kind": "accessor", "head": { "token": { - "position": [4, 19], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/invocation/invocation_instantiation_generics.2.out.json b/projects/compiler/test/typechecker/invocation/invocation_instantiation_generics.2.out.json index a2c245bf..82c08f94 100644 --- a/projects/compiler/test/typechecker/invocation/invocation_instantiation_generics.2.out.json +++ b/projects/compiler/test/typechecker/invocation/invocation_instantiation_generics.2.out.json @@ -64,7 +64,7 @@ "kind": "accessor", "head": { "token": { - "position": [3, 11], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/lambda/lambda.1.out.json b/projects/compiler/test/typechecker/lambda/lambda.1.out.json index e815d42b..0b899c75 100644 --- a/projects/compiler/test/typechecker/lambda/lambda.1.out.json +++ b/projects/compiler/test/typechecker/lambda/lambda.1.out.json @@ -551,7 +551,7 @@ "kind": "accessor", "head": { "token": { - "position": [3, 31], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/map/map.out.json b/projects/compiler/test/typechecker/map/map.out.json index 9f5d6912..3d882ca3 100644 --- a/projects/compiler/test/typechecker/map/map.out.json +++ b/projects/compiler/test/typechecker/map/map.out.json @@ -944,7 +944,7 @@ "kind": "accessor", "head": { "token": { - "position": [9, 48], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -1203,7 +1203,7 @@ "kind": "accessor", "head": { "token": { - "position": [11, 36], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -1252,7 +1252,7 @@ "kind": "accessor", "head": { "token": { - "position": [11, 43], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/set/set.out.json b/projects/compiler/test/typechecker/set/set.out.json index a782cb5b..5260b9b3 100644 --- a/projects/compiler/test/typechecker/set/set.out.json +++ b/projects/compiler/test/typechecker/set/set.out.json @@ -512,7 +512,7 @@ "kind": "accessor", "head": { "token": { - "position": [7, 32], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -666,7 +666,7 @@ "kind": "accessor", "head": { "token": { - "position": [8, 23], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -1069,7 +1069,7 @@ "kind": "accessor", "head": { "token": { - "position": [11, 21], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -1368,7 +1368,7 @@ "kind": "accessor", "head": { "token": { - "position": [12, 33], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -1605,7 +1605,7 @@ "kind": "accessor", "head": { "token": { - "position": [14, 32], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" @@ -2076,7 +2076,7 @@ "kind": "accessor", "head": { "token": { - "position": [18, 14], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/tuple/tuple.out.json b/projects/compiler/test/typechecker/tuple/tuple.out.json index 9529d189..ea3c23ef 100644 --- a/projects/compiler/test/typechecker/tuple/tuple.out.json +++ b/projects/compiler/test/typechecker/tuple/tuple.out.json @@ -217,7 +217,7 @@ "kind": "accessor", "head": { "token": { - "position": [2, 27], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/while/while.2.out.json b/projects/compiler/test/typechecker/while/while.2.out.json index ca1f39bd..5f5253b3 100644 --- a/projects/compiler/test/typechecker/while/while.2.out.json +++ b/projects/compiler/test/typechecker/while/while.2.out.json @@ -56,7 +56,7 @@ "kind": "accessor", "head": { "token": { - "position": [1, 15], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/compiler/test/typechecker/while/while.3.out.json b/projects/compiler/test/typechecker/while/while.3.out.json index 80505e1e..6c91b7ac 100644 --- a/projects/compiler/test/typechecker/while/while.3.out.json +++ b/projects/compiler/test/typechecker/while/while.3.out.json @@ -56,7 +56,7 @@ "kind": "accessor", "head": { "token": { - "position": [1, 15], + "position": [0, 0], "kind": { "name": "Ident", "value": "Option" diff --git a/projects/lsp/src/language_service.abra b/projects/lsp/src/language_service.abra index 9b5f5a2b..7083dbfd 100644 --- a/projects/lsp/src/language_service.abra +++ b/projects/lsp/src/language_service.abra @@ -1,7 +1,7 @@ import "fs" as fs import JsonValue from "json" import log from "./log" -import ModuleLoader, Project, Typechecker, TypecheckerErrorKind, IdentifierKindMeta from "../../compiler/src/typechecker" +import ModuleLoader, Project, Typechecker, TypecheckerErrorKind, IdentifierMeta, IdentifierKindMeta, IdentifierMetaImport from "../../compiler/src/typechecker" import RequestMessage, NotificationMessage, ResponseMessage, ResponseResult, ResponseError, ResponseErrorCode, ServerCapabilities, TextDocumentSyncOptions, TextDocumentSyncKind, SaveOptions, ServerInfo, TextDocumentItem, TextDocumentIdentifier, VersionedTextDocumentIdentifier, TextDocumentContentChangeEvent, Diagnostic, DiagnosticSeverity, Position, Range, MarkupContent, MarkupKind from "./lsp_spec" export val contentLengthHeader = "Content-Length: " @@ -49,6 +49,7 @@ export type AbraLanguageService { save: Some(SaveOptions(includeText: Some(false))) )), hoverProvider: Some(true), + definitionProvider: Some(true), ), serverInfo: ServerInfo(name: "abra-lsp", version: Some("0.0.1")) ) @@ -58,43 +59,62 @@ export type AbraLanguageService { func _hover(self, id: Int, textDocument: TextDocumentIdentifier, position: Position): ResponseMessage { // todo: what happens if it's not a `file://` uri? val filePath = textDocument.uri.replaceAll("file://", "") - val module = if self._project.modules[filePath] |mod| mod else return ResponseMessage.Success(id: id, result: None) - val line = position.line - val identsByLine = if module.identsByLine[line] |idents| idents else return ResponseMessage.Success(id: id, result: None) - for (colStart, colEnd, ident) in identsByLine { - if colStart <= position.character && position.character <= colEnd { - val message = match ident.kind { - IdentifierKindMeta.Variable(mutable, typeRepr) => { - val prefix = if mutable "var" else "val" - "$prefix ${ident.name}: $typeRepr" - } - IdentifierKindMeta.Function(typeParams, params, returnTypeRepr) => { - val generics = if typeParams.isEmpty() "" else "<${typeParams.join(", ")}>" - "func ${ident.name}$generics(${params.join(", ")}): $returnTypeRepr" - } - IdentifierKindMeta.Type(isEnum, typeParams) => { - val prefix = if isEnum "enum" else "type" - val generics = if typeParams.isEmpty() "" else "<${typeParams.join(", ")}>" - "$prefix ${ident.name}$generics" - } - } - val lines = ["```abra", message, "```"] + val (line, identColStart, identColEnd, ident) = if self._findIdentAtPosition(filePath, position) |ident| ident else { + return ResponseMessage.Success(id: id, result: None) + } - if ident.importedFrom |modName| { - lines.push("Imported from `$modName`") - } + val message = match ident.kind { + IdentifierKindMeta.Variable(mutable, typeRepr) => { + val prefix = if mutable "var" else "val" + "$prefix ${ident.name}: $typeRepr" + } + IdentifierKindMeta.Function(typeParams, params, returnTypeRepr) => { + val generics = if typeParams.isEmpty() "" else "<${typeParams.join(", ")}>" + "func ${ident.name}$generics(${params.join(", ")}): $returnTypeRepr" + } + IdentifierKindMeta.Type(isEnum, typeParams) => { + val prefix = if isEnum "enum" else "type" + val generics = if typeParams.isEmpty() "" else "<${typeParams.join(", ")}>" + "$prefix ${ident.name}$generics" + } + } + val lines = ["```abra", message, "```"] + + if ident.importedFrom |modName| { + lines.push("Imported from `$modName`") + } - val value = lines.join("\\n") + val value = lines.join("\\n") - val range = Range(start: Position(line: line, character: colStart), end: Position(line: line, character: colEnd)) - val contents = MarkupContent(kind: MarkupKind.Markdown, value: value) - val result = ResponseResult.Hover(contents: contents, range: Some(range)) - return ResponseMessage.Success(id: id, result: Some(result)) + val range = Range(start: Position(line: line, character: identColStart), end: Position(line: line, character: identColEnd)) + val contents = MarkupContent(kind: MarkupKind.Markdown, value: value) + val result = ResponseResult.Hover(contents: contents, range: Some(range)) + ResponseMessage.Success(id: id, result: Some(result)) + } + + func _goToDefinition(self, id: Int, textDocument: TextDocumentIdentifier, position: Position): ResponseMessage { + // todo: what happens if it's not a `file://` uri? + val filePath = textDocument.uri.replaceAll("file://", "") + + val ident = if self._findIdentAtPosition(filePath, position) |(_, _, _, ident)| ident else return ResponseMessage.Success(id: id, result: None) + val result = if ident.definitionPosition |pos| { + val line = pos.line - 1 + val character = pos.col - 1 + val range = Range(start: Position(line: line, character: character), end: Position(line: line, character: character)) + // val uri = if ident.importedFrom |mod| "file://$mod" else textDocument.uri + val definitionFilePath = match ident.importedFrom { + None => filePath + IdentifierMetaImport.Prelude => self._moduleLoader.stdRoot + "/prelude.abra" + IdentifierMetaImport.Module(mod) => mod } + val uri = "file://$definitionFilePath" + Some(ResponseResult.Definition(uri: uri, range: range)) + } else { + None } - ResponseMessage.Success(id: id, result: None) + ResponseMessage.Success(id: id, result: result) } // Notification handlers @@ -192,12 +212,27 @@ export type AbraLanguageService { } } + func _findIdentAtPosition(self, filePath: String, position: Position): (Int, Int, Int, IdentifierMeta)? { + val module = if self._project.modules[filePath] |mod| mod else return None + val line = position.line + val identsByLine = if module.identsByLine[line] |idents| idents else return None + + for (colStart, colEnd, ident) in identsByLine { + if colStart <= position.character && position.character <= colEnd { + return Some((line, colStart, colEnd, ident)) + } + } + + None + } + // Dispatch func handleRequest(self, req: RequestMessage): ResponseMessage { match req { RequestMessage.Initialize(id, processId, rootPath) => self._initialize(id, processId, rootPath) RequestMessage.Hover(id, textDocument, position) => self._hover(id, textDocument, position) + RequestMessage.Definition(id, textDocument, position) => self._goToDefinition(id, textDocument, position) } } diff --git a/projects/lsp/src/lsp_spec.abra b/projects/lsp/src/lsp_spec.abra index 062f85a1..595f83c4 100644 --- a/projects/lsp/src/lsp_spec.abra +++ b/projects/lsp/src/lsp_spec.abra @@ -4,6 +4,7 @@ import JsonValue, JsonError, JsonObject from "json" export enum RequestMessage { Initialize(id: Int, processId: Int?, rootPath: String?) Hover(id: Int, textDocument: TextDocumentIdentifier, position: Position) + Definition(id: Int, textDocument: TextDocumentIdentifier, position: Position) func fromJson(json: JsonValue): Result { val obj = try json.asObject() @@ -36,6 +37,16 @@ export enum RequestMessage { Ok(Some(RequestMessage.Hover(id: id, textDocument: textDocument, position: position))) } + "textDocument/definition" => { + val params = try obj.getObjectRequired("params") + val textDocumentObj = try params.getValueRequired("textDocument") + val textDocument = try TextDocumentIdentifier.fromJson(textDocumentObj) + + val positionObj = try params.getValueRequired("position") + val position = try Position.fromJson(positionObj) + + Ok(Some(RequestMessage.Definition(id: id, textDocument: textDocument, position: position))) + } else => { log.writeln("Error: Unimplemented RequestMessage method '$method'") @@ -140,13 +151,16 @@ export enum ResponseMessage { export enum ResponseResult { Initialize(capabilities: ServerCapabilities, serverInfo: ServerInfo) Hover(contents: MarkupContent, range: Range?) + // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition + // Note: the actual response body for textDocument/definition requests is just a `Location` type, which is flattened here + Definition(uri: String, range: Range) func toJson(self): JsonValue { val obj = match self { ResponseResult.Initialize(capabilities, serverInfo) => { JsonObject(_map: { "capabilities": capabilities.toJson(), - "serverInfo": serverInfo.toJson() + "serverInfo": serverInfo.toJson(), }) } ResponseResult.Hover(contents, range) => { @@ -160,6 +174,12 @@ export enum ResponseResult { obj } + ResponseResult.Definition(uri, range) => { + JsonObject(_map: { + "uri": JsonValue.String(uri), + "range": range.toJson(), + }) + } } JsonValue.Object(obj) @@ -212,6 +232,7 @@ export type ServerCapabilities { textDocumentSync: TextDocumentSyncOptions? = None diagnosticProvider: DiagnosticOptions? = None hoverProvider: Bool? = None + definitionProvider: Bool? = None func toJson(self): JsonValue { val obj = JsonObject() @@ -228,6 +249,10 @@ export type ServerCapabilities { obj.set("hoverProvider", JsonValue.Boolean(hp)) } + if self.definitionProvider |dp| { + obj.set("definitionProvider", JsonValue.Boolean(dp)) + } + JsonValue.Object(obj) } }