diff options
| author | DanielGavin <danielgavin5@hotmail.com> | 2020-11-09 23:51:48 +0100 |
|---|---|---|
| committer | DanielGavin <danielgavin5@hotmail.com> | 2020-11-09 23:51:48 +0100 |
| commit | f9416ac2e7ebef3b881305467edad22ea411603e (patch) | |
| tree | 514474c94842fec9f5051ff87adc143af3cd5220 /src/server | |
| parent | b8ca857f075763e2254a84135a72f1dcace5c74c (diff) | |
changed the way we find position context using the ast
Diffstat (limited to 'src/server')
| -rw-r--r-- | src/server/analysis.odin | 476 | ||||
| -rw-r--r-- | src/server/documents.odin | 17 | ||||
| -rw-r--r-- | src/server/requests.odin | 65 | ||||
| -rw-r--r-- | src/server/types.odin | 44 | ||||
| -rw-r--r-- | src/server/unmarshal.odin | 21 |
5 files changed, 496 insertions, 127 deletions
diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 7c1977c..1996041 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -7,38 +7,26 @@ import "core:fmt" import "core:log" import "core:strings" import "core:path" +import "core:mem" import "shared:common" import "shared:index" - -DocumentPositionContextVariableDotVariableValue :: struct { - prefix: string, - postfix: string, -}; - -DocumentPositionContextGlobalValue :: struct { - name: string, -}; - -DocumentPositionContextVariableDotValue :: struct { - prefix: string, -}; - -DocumentPositionContextUnknownValue :: struct { - -} - -DocumentPositionContextValue :: union { - DocumentPositionContextVariableDotValue, - DocumentPositionContextGlobalValue, - DocumentPositionContextUnknownValue, - DocumentPositionContextVariableDotVariableValue, +DocumentPositionContextHint :: enum { + Completion, + SignatureHelp, + Definition, }; DocumentPositionContext :: struct { - value: DocumentPositionContextValue, + position: common.AbsolutePosition, + function: ^ast.Node, //used to help with type resolving in function scope + selector: ^ast.Node, //used for completion + identifier: ^ast.Node, + field: ^ast.Node, //used for completion + call: ^ast.Node, //used for signature help + hint: DocumentPositionContextHint, }; @@ -47,127 +35,70 @@ tokenizer_error_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) { } -/* - Figure out what exactly is at the given position and whether it is in a function, struct, etc. -*/ -get_document_position_context :: proc(document: ^Document, position: common.Position) -> (DocumentPositionContext, bool) { +get_definition_location :: proc(document: ^Document, position: common.Position) -> (common.Location, bool) { - position_context: DocumentPositionContext; + location: common.Location; - absolute_position, ok := common.get_absolute_position(position, document.text); + position_context, ok := get_document_position_context(document, position, .Definition); if !ok { - return position_context, false; + return location, false; } + symbol: index.Symbol; - //Using the ast is not really viable since the code may be broken code - t: tokenizer.Tokenizer; - - tokenizer.init(&t, document.text, document.uri.path, tokenizer_error_handler); - - stack := make([dynamic] tokenizer.Token, context.temp_allocator); - - current_token: tokenizer.Token; - last_token: tokenizer.Token; - - struct_or_package_dotted: bool; - struct_or_package: tokenizer.Token; - - last_label: bool; - - /* - Idea is to push and pop into braces, brackets, etc, and use the final stack to infer context - */ - - for true { + if position_context.selector != nil && position_context.field != nil { - current_token = tokenizer.scan(&t); + selector: string; - #partial switch current_token.kind { - case .Period: - if last_token.kind == .Ident { - struct_or_package_dotted = true; - struct_or_package = last_token; - } - case .Ident: - last_label = true; - case .EOF: - last_label = false; - break; + switch v in position_context.selector.derived { + case ast.Ident: + selector = v.name; case: - struct_or_package_dotted = false; - last_label = false; + return location, false; } - if current_token.pos.offset+len(current_token.text) >= absolute_position { - break; - } + field: string; - last_token = current_token; - } + switch v in position_context.field.derived { + case ast.Ident: + field = v.name; + case: + return location, false; + } - #partial switch current_token.kind { - case .Ident: - if struct_or_package_dotted { - position_context.value = DocumentPositionContextVariableDotVariableValue { - prefix = struct_or_package.text, - postfix = current_token.text, - }; - } - else { - position_context.value = DocumentPositionContextGlobalValue { - name = current_token.text, - }; - } - case .Period: - if last_label { - position_context.value = DocumentPositionContextVariableDotValue { - prefix = last_token.text, - }; - } - else { - position_context.value = DocumentPositionContextUnknownValue { - - }; - } + symbol, ok = index.lookup(strings.concatenate({selector, field}, context.temp_allocator)); - case: - position_context.value = DocumentPositionContextUnknownValue { + if !ok { + return location, false; + } - }; } - log.info(position_context); + else if position_context.identifier != nil { - return position_context, true; -} + field: string; + switch v in position_context.field.derived { + case ast.Ident: + field = v.name; + case: + return location, false; + } -get_definition_location :: proc(document: ^Document, position: common.Position) -> (common.Location, bool) { + symbol, ok = index.lookup(strings.concatenate({document.ast.pkg_name, field}, context.temp_allocator)); - location: common.Location; + if !ok { + return location, false; + } - position_context, ok := get_document_position_context(document, position); - if !ok { - return location, false; } - symbol: index.Symbol; - - #partial switch v in position_context.value { - case DocumentPositionContextVariableDotVariableValue: - symbol, ok = index.lookup(strings.concatenate({v.prefix, v.postfix}, context.temp_allocator)); - case DocumentPositionContextGlobalValue: - symbol, ok = index.lookup(strings.concatenate({document.ast.pkg_name, v.name}, context.temp_allocator)); - case: + else { return location, false; } - if !ok { - return location, false; - } location.range = symbol.range; location.uri = symbol.uri; @@ -180,22 +111,44 @@ get_completion_list :: proc(document: ^Document, position: common.Position) -> ( list: CompletionList; + position_context, ok := get_document_position_context(document, position, .Completion); - position_context, ok := get_document_position_context(document, position); symbols: [] index.Symbol; + //if we have format "selector.access" with plain identifiers + if position_context.selector != nil && position_context.field != nil { + + selector: string; + + switch v in position_context.selector.derived { + case ast.Ident: + selector = v.name; + case: + return list, false; + } + + field: string; + + switch v in position_context.field.derived { + case ast.Ident: + field = v.name; + case: + return list, false; + } + + symbols, ok = index.fuzzy_search(field, {selector}); + + if !ok { + return list, false; + } - #partial switch v in position_context.value { - case DocumentPositionContextVariableDotVariableValue: - symbols, ok = index.fuzzy_search(v.postfix, {v.prefix}); - case DocumentPositionContextVariableDotValue: - symbols, ok = index.fuzzy_search("", {v.prefix}); } - if !ok { + else { return list, false; } + list.items = make([] CompletionItem, len(symbols), context.temp_allocator); for symbol, i in symbols { @@ -204,4 +157,281 @@ get_completion_list :: proc(document: ^Document, position: common.Position) -> ( } return list, true; +} + +get_signature_information :: proc(document: ^Document, position: common.Position) -> (SignatureHelp, bool) { + + signature_help: SignatureHelp; + + position_context, ok := get_document_position_context(document, position, .SignatureHelp); + + return signature_help, true; +} + + +/* + Figure out what exactly is at the given position and whether it is in a function, struct, etc. +*/ +get_document_position_context :: proc(document: ^Document, position: common.Position, hint: DocumentPositionContextHint) -> (DocumentPositionContext, bool) { + + position_context: DocumentPositionContext; + + position_context.hint = hint; + + absolute_position, ok := common.get_absolute_position(position, document.text); + + if !ok { + log.error("failed to get absolute position"); + return position_context, false; + } + + position_context.position = absolute_position; + + for decl in document.ast.decls { + get_document_position(decl, &position_context); + } + + + //fmt.println(position_context); + + return position_context, true; +} + +get_document_position :: proc{ + get_document_position_array, + get_document_position_dynamic_array, + get_document_position_node, +}; + +get_document_position_array :: proc(array: $A/[]^$T, position_context: ^DocumentPositionContext) { + + for elem, i in array { + get_document_position(elem, position_context); + } + +} + +get_document_position_dynamic_array :: proc(array: $A/[dynamic]^$T, position_context: ^DocumentPositionContext) { + + for elem, i in array { + get_document_position(elem, position_context); + } + +} + +position_in_node :: proc(node: ^ast.Node, position: common.AbsolutePosition) -> bool { + return node != nil && node.pos.offset <= position && position <= node.end.offset; +} + +get_document_position_node :: proc(node: ^ast.Node, position_context: ^DocumentPositionContext) { + + using ast; + + if node == nil { + return; + } + + if !(node.pos.offset <= position_context.position && position_context.position <= node.end.offset) { + return; + } + + switch n in node.derived { + case Bad_Expr: + case Ident: + position_context.identifier = node; + return; + case Implicit: + case Undef: + case Basic_Lit: + case Ellipsis: + get_document_position(n.expr, position_context); + case Proc_Lit: + get_document_position(n.type, position_context); + + if position_in_node(n.body, position_context.position) { + position_context.function = node; + get_document_position(n.body, position_context); + return; + } + + case Comp_Lit: + get_document_position(n.type, position_context); + get_document_position(n.elems, position_context); + case Tag_Expr: + get_document_position(n.expr, position_context); + case Unary_Expr: + get_document_position(n.expr, position_context); + case Binary_Expr: + get_document_position(n.left, position_context); + get_document_position(n.right, position_context); + case Paren_Expr: + get_document_position(n.expr, position_context); + case Call_Expr: + get_document_position(n.expr, position_context); + get_document_position(n.args, position_context); + case Selector_Expr: + get_document_position(n.expr, position_context); + if position_in_node(n.field, position_context.position) { + position_context.selector = n.expr; + position_context.field = n.field; + get_document_position(n.field, position_context); + return; + } + + case Index_Expr: + get_document_position(n.expr, position_context); + get_document_position(n.index, position_context); + case Deref_Expr: + get_document_position(n.expr, position_context); + case Slice_Expr: + get_document_position(n.expr, position_context); + get_document_position(n.low, position_context); + get_document_position(n.high, position_context); + case Field_Value: + get_document_position(n.field, position_context); + get_document_position(n.value, position_context); + case Ternary_Expr: + get_document_position(n.cond, position_context); + get_document_position(n.x, position_context); + get_document_position(n.y, position_context); + case Ternary_If_Expr: + get_document_position(n.x, position_context); + get_document_position(n.cond, position_context); + get_document_position(n.y, position_context); + case Ternary_When_Expr: + get_document_position(n.x, position_context); + get_document_position(n.cond, position_context); + get_document_position(n.y, position_context); + case Type_Assertion: + get_document_position(n.expr, position_context); + get_document_position(n.type, position_context); + case Type_Cast: + get_document_position(n.type, position_context); + get_document_position(n.expr, position_context); + case Auto_Cast: + get_document_position(n.expr, position_context); + case Bad_Stmt: + case Empty_Stmt: + case Expr_Stmt: + get_document_position(n.expr, position_context); + case Tag_Stmt: + r := cast(^Expr_Stmt)node; + get_document_position(r.expr, position_context); + case Assign_Stmt: + get_document_position(n.lhs, position_context); + get_document_position(n.rhs, position_context); + case Block_Stmt: + get_document_position(n.label, position_context); + get_document_position(n.stmts, position_context); + case If_Stmt: + get_document_position(n.label, position_context); + get_document_position(n.init, position_context); + get_document_position(n.cond, position_context); + get_document_position(n.body, position_context); + get_document_position(n.else_stmt, position_context); + case When_Stmt: + get_document_position(n.cond, position_context); + get_document_position(n.body, position_context); + get_document_position(n.else_stmt, position_context); + case Return_Stmt: + get_document_position(n.results, position_context); + case Defer_Stmt: + get_document_position(n.stmt, position_context); + case For_Stmt: + get_document_position(n.label, position_context); + get_document_position(n.init, position_context); + get_document_position(n.cond, position_context); + get_document_position(n.post, position_context); + get_document_position(n.body, position_context); + case Range_Stmt: + get_document_position(n.label, position_context); + get_document_position(n.val0, position_context); + get_document_position(n.val1, position_context); + get_document_position(n.expr, position_context); + get_document_position(n.body, position_context); + case Case_Clause: + get_document_position(n.list, position_context); + get_document_position(n.body, position_context); + case Switch_Stmt: + get_document_position(n.label, position_context); + get_document_position(n.init, position_context); + get_document_position(n.cond, position_context); + get_document_position(n.body, position_context); + case Type_Switch_Stmt: + get_document_position(n.label, position_context); + get_document_position(n.tag, position_context); + get_document_position(n.expr, position_context); + get_document_position(n.body, position_context); + case Branch_Stmt: + get_document_position(n.label, position_context); + case Using_Stmt: + get_document_position(n.list, position_context); + case Bad_Decl: + case Value_Decl: + get_document_position(n.attributes, position_context); + get_document_position(n.names, position_context); + get_document_position(n.type, position_context); + get_document_position(n.values, position_context); + case Package_Decl: + case Import_Decl: + case Foreign_Block_Decl: + get_document_position(n.attributes, position_context); + get_document_position(n.foreign_library, position_context); + get_document_position(n.body, position_context); + case Foreign_Import_Decl: + get_document_position(n.name, position_context); + case Proc_Group: + get_document_position(n.args, position_context); + case Attribute: + get_document_position(n.elems, position_context); + case Field: + get_document_position(n.names, position_context); + get_document_position(n.type, position_context); + get_document_position(n.default_value, position_context); + case Field_List: + get_document_position(n.list, position_context); + case Typeid_Type: + get_document_position(n.specialization, position_context); + case Helper_Type: + get_document_position(n.type, position_context); + case Distinct_Type: + get_document_position(n.type, position_context); + case Opaque_Type: + get_document_position(n.type, position_context); + case Poly_Type: + get_document_position(n.type, position_context); + get_document_position(n.specialization, position_context); + case Proc_Type: + get_document_position(n.params, position_context); + get_document_position(n.results, position_context); + case Pointer_Type: + get_document_position(n.elem, position_context); + case Array_Type: + get_document_position(n.len, position_context); + get_document_position(n.elem, position_context); + case Dynamic_Array_Type: + get_document_position(n.elem, position_context); + case Struct_Type: + get_document_position(n.poly_params, position_context); + get_document_position(n.align, position_context); + get_document_position(n.fields, position_context); + case Union_Type: + get_document_position(n.poly_params, position_context); + get_document_position(n.align, position_context); + get_document_position(n.variants, position_context); + case Enum_Type: + get_document_position(n.base_type, position_context); + get_document_position(n.fields, position_context); + case Bit_Field_Type: + get_document_position(n.fields, position_context); + case Bit_Set_Type: + get_document_position(n.elem, position_context); + get_document_position(n.underlying, position_context); + case Map_Type: + get_document_position(n.key, position_context); + get_document_position(n.value, position_context); + case: + fmt.panicf("Unhandled node kind: %T", n); + } + }
\ No newline at end of file diff --git a/src/server/documents.odin b/src/server/documents.odin index 4ccba6e..d349eba 100644 --- a/src/server/documents.odin +++ b/src/server/documents.odin @@ -392,6 +392,18 @@ parse_document :: proc(document: ^Document, config: ^common.Config) -> ([] Parse parser.parse_file(&p, &document.ast); /* + fmt.println(); + fmt.println(); + + for decl in document.ast.decls { + common.print_ast(decl, 0, document.ast.src); + } + + fmt.println(); + fmt.println(); + */ + + /* if document.imports != nil { delete(document.imports); delete(document.package_name); @@ -425,4 +437,9 @@ parse_document :: proc(document: ^Document, config: ^common.Config) -> ([] Parse */ return current_errors[:], true; +} + + +free_ast_node :: proc(file: ^ast.Node) { + }
\ No newline at end of file diff --git a/src/server/requests.odin b/src/server/requests.odin index 0c52f65..bc7c193 100644 --- a/src/server/requests.odin +++ b/src/server/requests.odin @@ -168,7 +168,8 @@ handle_request :: proc(request: json.Value, config: ^common.Config, writer: ^Wri "textDocument/didClose" = notification_did_close, "textDocument/didSave" = notification_did_save, "textDocument/definition" = request_definition, - "textDocument/completion" = request_completion}; + "textDocument/completion" = request_completion, + "textDocument/signatureHelp" = request_signature_help}; fn: proc(json.Value, RequestId, ^common.Config, ^Writer) -> common.Error; fn, ok = call_map[method]; @@ -226,7 +227,11 @@ request_initialize :: proc(params: json.Value, id: RequestId, config: ^common.Co } } - triggerCharacters := [] string { "." }; + config.signature_offset_support = initialize_params.capabilities.textDocument.signatureHelp.signatureInformation.parameterInformation.labelOffsetSupport; + + + completionTriggerCharacters := [] string { "." }; + signatureTriggerCharacters := [] string { "(" }; response := make_response_message( params = ResponseInitializeParams { @@ -238,7 +243,10 @@ request_initialize :: proc(params: json.Value, id: RequestId, config: ^common.Co definitionProvider = true, completionProvider = CompletionOptions { resolveProvider = false, - triggerCharacters = triggerCharacters, + triggerCharacters = completionTriggerCharacters, + }, + signatureHelpProvider = SignatureHelpOptions { + triggerCharacters = signatureTriggerCharacters, }, }, }, @@ -344,6 +352,57 @@ request_completion :: proc(params: json.Value, id: RequestId, config: ^common.Co return .None; } +request_signature_help :: proc(params: json.Value, id: RequestId, config: ^common.Config, writer: ^Writer) -> common.Error { + + params_object, ok := params.value.(json.Object); + + if !ok { + return .ParseError; + } + + signature_params: SignatureHelpParams; + + if unmarshal(params, signature_params, context.temp_allocator) != .None { + return .ParseError; + } + + document := document_get(signature_params.textDocument.uri); + + if document == nil { + return .InternalError; + } + + parameters := [] ParameterInformation { + { + label = {0, 4}, + }, + }; + + + signatures := [] SignatureInformation { + { + label = "test", + parameters = parameters, + }, + }; + + help := SignatureHelp { + activeSignature = 0, + activeParameter = 0, + signatures = signatures, + }; + + get_signature_information(document, signature_params.position); + + response := make_response_message( + params = help, + id = id, + ); + + send_response(response, writer); + + return .None; +} notification_exit :: proc(params: json.Value, id: RequestId, config: ^common.Config, writer: ^Writer) -> common.Error { config.running = false; diff --git a/src/server/types.odin b/src/server/types.odin index 2199601..d957041 100644 --- a/src/server/types.odin +++ b/src/server/types.odin @@ -20,6 +20,7 @@ ResponseParams :: union { rawptr, common.Location, CompletionList, + SignatureHelp, }; ResponseMessage :: struct { @@ -80,6 +81,7 @@ ServerCapabilities :: struct { textDocumentSync: TextDocumentSyncOptions, definitionProvider: bool, completionProvider: CompletionOptions, + signatureHelpProvider: SignatureHelpOptions, }; CompletionOptions :: struct { @@ -95,12 +97,32 @@ HoverClientCapabilities :: struct { TextDocumentClientCapabilities :: struct { completion: CompletionClientCapabilities, hover: HoverClientCapabilities, + signatureHelp: SignatureHelpClientCapabilities, }; CompletionClientCapabilities :: struct { }; +ParameterInformationCapabilities :: struct { + labelOffsetSupport: bool, +}; + +SignatureInformationCapabilities :: struct { + parameterInformation: ParameterInformationCapabilities, +}; + +SignatureHelpClientCapabilities :: struct { + dynamicRegistration: bool, + signatureInformation: SignatureInformationCapabilities, + contextSupport: bool, +}; + +SignatureHelpOptions :: struct { + triggerCharacters: [] string, + retriggerCharacters: [] string, +}; + ClientCapabilities :: struct { textDocument: TextDocumentClientCapabilities, }; @@ -164,6 +186,11 @@ TextDocumentPositionParams :: struct { position: common.Position, }; +SignatureHelpParams :: struct { + textDocument: TextDocumentIdentifier, + position: common.Position, +}; + CompletionParams :: struct { textDocument: TextDocumentIdentifier, position: common.Position, @@ -210,4 +237,19 @@ CompletionList :: struct { TextDocumentSyncOptions :: struct { openClose: bool, change: int, -}
\ No newline at end of file +}; + +SignatureHelp :: struct { + signatures: [] SignatureInformation, + activeSignature: int, + activeParameter: int, +}; + +SignatureInformation :: struct { + label: string, + parameters: [] ParameterInformation, +}; + +ParameterInformation :: struct { + label: [2] int, +};
\ No newline at end of file diff --git a/src/server/unmarshal.odin b/src/server/unmarshal.odin index c658848..828e62c 100644 --- a/src/server/unmarshal.odin +++ b/src/server/unmarshal.odin @@ -8,6 +8,10 @@ import "core:fmt" //Note(Daniel, investigate if you can use some sort of attribute not to be forced to have the same variable name as the json name) +/* + Right now union handling is type specific so you can only have one struct type, int type, etc. + */ + unmarshal :: proc(json_value: json.Value, v: any, allocator := context.allocator) -> json.Marshal_Error { using runtime; @@ -33,6 +37,23 @@ unmarshal :: proc(json_value: json.Value, v: any, allocator := context.allocator return ret; } } + + case Type_Info_Union: + + //Note(Daniel, THIS IS REALLY SCUFFED. Need to talk to gingerbill about unmarshalling unions) + + //This only works for unions with one object - made to handle optionals + tag_ptr := uintptr(v.data) + variant.tag_offset; + tag_any := any{rawptr(tag_ptr), variant.tag_type.id}; + + not_optional := 1; + + mem.copy(cast(rawptr)tag_ptr, ¬_optional, size_of(variant.tag_type)); + + id := variant.variants[0].id; + + unmarshal(json_value, any{v.data, id}); + } case json.Array: #partial switch variant in type_info.variant { |