aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorDanielGavin <danielgavin5@hotmail.com>2020-11-09 23:51:48 +0100
committerDanielGavin <danielgavin5@hotmail.com>2020-11-09 23:51:48 +0100
commitf9416ac2e7ebef3b881305467edad22ea411603e (patch)
tree514474c94842fec9f5051ff87adc143af3cd5220 /src/server
parentb8ca857f075763e2254a84135a72f1dcace5c74c (diff)
changed the way we find position context using the ast
Diffstat (limited to 'src/server')
-rw-r--r--src/server/analysis.odin476
-rw-r--r--src/server/documents.odin17
-rw-r--r--src/server/requests.odin65
-rw-r--r--src/server/types.odin44
-rw-r--r--src/server/unmarshal.odin21
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, &not_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 {