diff options
| author | DanielGavin <danielgavin5@hotmail.com> | 2021-03-31 20:59:02 +0200 |
|---|---|---|
| committer | DanielGavin <danielgavin5@hotmail.com> | 2021-03-31 20:59:02 +0200 |
| commit | a3bf952b19d210b9578c40dffe5ec48c76cca49c (patch) | |
| tree | 203da390144ab4b9b91e696db1bd45ac65c0f520 /src | |
| parent | 676f27e656073ab40ccbb19bb3470b8801e421e6 (diff) | |
start work on package completion
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/config.odin | 2 | ||||
| -rw-r--r-- | src/server/analysis.odin | 16 | ||||
| -rw-r--r-- | src/server/completion.odin | 98 | ||||
| -rw-r--r-- | src/server/requests.odin | 14 | ||||
| -rw-r--r-- | src/server/types.odin | 5 | ||||
| -rw-r--r-- | src/server/unmarshal.odin | 14 |
6 files changed, 135 insertions, 14 deletions
diff --git a/src/common/config.odin b/src/common/config.odin index df6ddb1..432794f 100644 --- a/src/common/config.odin +++ b/src/common/config.odin @@ -11,3 +11,5 @@ Config :: struct { debug_single_thread: bool, enable_semantic_tokens: bool, //This will be removed when vscode client stops sending me semantic tokens after disabling it in requests initialize. } + +config: Config;
\ No newline at end of file diff --git a/src/server/analysis.odin b/src/server/analysis.odin index 0ad68cc..5d7cc40 100644 --- a/src/server/analysis.odin +++ b/src/server/analysis.odin @@ -59,6 +59,7 @@ DocumentPositionContext :: struct { abort_completion: bool, hint: DocumentPositionContextHint, global_lhs_stmt: bool, + import_stmt: ^ast.Import_Decl, } DocumentLocal :: struct { @@ -2069,7 +2070,14 @@ get_document_position_context :: proc(document: ^Document, position: common.Posi } } - if !exists_in_decl { + for import_stmt in document.ast.imports { + if position_in_node(import_stmt, position_context.position) { + position_context.import_stmt = import_stmt; + break; + } + } + + if !exists_in_decl && position_context.import_stmt == nil { position_context.abort_completion = true; } @@ -2162,7 +2170,7 @@ fallback_position_context_completion :: proc(document: ^Document, position: comm c == '}' || c == '^' || c == ':' || c == '\n' || c == '\r' || c == '=' || c == '<' || c == '-' || c == '!' || - c == '+' || c == '&' { + c == '+' || c == '&'|| c == '|' { start = i + 1; break; } else if c == '>' { @@ -2196,12 +2204,12 @@ fallback_position_context_completion :: proc(document: ^Document, position: comm return; } + s := string(position_context.file.src[begin_offset:end_offset]); + if !partial_arrow { only_whitespaces := true; - s := string(position_context.file.src[begin_offset:end_offset]); - for r in s { if !strings.is_space(r) { only_whitespaces = false; diff --git a/src/server/completion.odin b/src/server/completion.odin index 7dac1b9..4494bf4 100644 --- a/src/server/completion.odin +++ b/src/server/completion.odin @@ -12,6 +12,8 @@ import "core:strconv" import "core:path/filepath" import "core:sort" import "core:slice" +import "core:os" + import "shared:common" import "shared:index" @@ -23,9 +25,10 @@ Completion_Type :: enum { Identifier, Comp_Lit, Directive, + Package, } -get_completion_list :: proc(document: ^Document, position: common.Position) -> (CompletionList, bool) { +get_completion_list :: proc(document: ^Document, position: common.Position, completion_context: CompletionContext) -> (CompletionList, bool) { list: CompletionList; @@ -35,6 +38,10 @@ get_completion_list :: proc(document: ^Document, position: common.Position) -> ( return list, true; } + if position_context.import_stmt == nil && strings.contains_any(completion_context.triggerCharacter, "/:\"") { + return list, true; + } + ast_context := make_ast_context(document.ast, document.imports, document.package_name); get_globals(document.ast, &ast_context); @@ -64,6 +71,10 @@ get_completion_list :: proc(document: ^Document, position: common.Position) -> ( completion_type = .Implicit; } + if position_context.import_stmt != nil { + completion_type = .Package; + } + if position_context.switch_type_stmt != nil && position_context.case_clause != nil { if assign, ok := position_context.switch_type_stmt.tag.derived.(ast.Assign_Stmt); ok && assign.rhs != nil && len(assign.rhs) == 1 { @@ -90,6 +101,8 @@ get_completion_list :: proc(document: ^Document, position: common.Position) -> ( get_type_switch_Completion(&ast_context, &position_context, &list); case .Directive: get_directive_completion(&ast_context, &position_context, &list); + case .Package: + get_package_completion(&ast_context, &position_context, &list); } return list, true; @@ -938,6 +951,89 @@ get_identifier_completion :: proc(ast_context: ^AstContext, position_context: ^D } get_package_completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) { + + items := make([dynamic]CompletionItem, context.temp_allocator); + + list.isIncomplete = false; + + fullpath_length := len(position_context.import_stmt.fullpath); + + if fullpath_length <= 1 { + return; + } + + without_quotes := position_context.import_stmt.fullpath[1:fullpath_length-1]; + absolute_path := without_quotes; + colon_index := strings.index(without_quotes, ":"); + + if colon_index >= 0 { + c := without_quotes[0:colon_index]; + + if colon_index+1 < len(without_quotes) { + absolute_path = filepath.join(elems = {common.config.collections[c], filepath.dir(without_quotes[colon_index+1:], context.temp_allocator)}, allocator = context.temp_allocator); + } else { + absolute_path = common.config.collections[c]; + } + } else { + import_file_dir := filepath.dir(position_context.import_stmt.pos.file, context.temp_allocator); + import_dir := filepath.dir(without_quotes, context.temp_allocator); + absolute_path = filepath.join(elems = {import_file_dir, import_dir}, allocator = context.temp_allocator); + } + + if !strings.contains(position_context.import_stmt.fullpath, "/") && !strings.contains(position_context.import_stmt.fullpath, ":") { + + for key, _ in common.config.collections { + + item := CompletionItem { + detail = "collection", + label = key, + kind = .Module, + }; + + append(&items, item); + } + + } + + for pkg in search_for_packages(absolute_path) { + + item := CompletionItem { + detail = pkg, + label = filepath.base(pkg), + kind = .Folder, + }; + + if item.label[0] == '.' { + continue; + } + + append(&items, item); + } + + list.items = items[:]; +} + +search_for_packages :: proc(fullpath: string) -> [] string { + + packages := make([dynamic]string, context.temp_allocator); + + fh, err := os.open(fullpath); + + if err != 0 { + return {}; + } + + if files, err := os.read_dir(fh, 0, context.temp_allocator); err == 0 { + + for file in files { + if file.is_dir { + append(&packages, file.fullpath); + } + } + + } + + return packages[:]; } get_type_switch_Completion :: proc(ast_context: ^AstContext, position_context: ^DocumentPositionContext, list: ^CompletionList) { diff --git a/src/server/requests.odin b/src/server/requests.odin index 91cd073..06a6620 100644 --- a/src/server/requests.odin +++ b/src/server/requests.odin @@ -208,7 +208,7 @@ handle_request :: proc (request: json.Value, config: ^common.Config, writer: ^Wr log.error("No root object"); return false; } - + id: RequestId; id_value: json.Value; id_value, ok = root["id"]; @@ -399,7 +399,7 @@ request_initialize :: proc (task: ^common.Task) { enable_document_symbols: bool; enable_hover: bool; enable_format: bool; - + if len(config.workspace_folders) > 0 { //right now just look at the first workspace - TODO(daniel, add multiple workspace support) @@ -461,7 +461,7 @@ request_initialize :: proc (task: ^common.Task) { config.signature_offset_support = initialize_params.capabilities.textDocument.signatureHelp.signatureInformation.parameterInformation.labelOffsetSupport; - completionTriggerCharacters := []string {".", ">", "#"}; + completionTriggerCharacters := []string {".", ">", "#", "\"", "/", ":"}; signatureTriggerCharacters := []string {"("}; token_type := type_info_of(SemanticTokenTypes).variant.(runtime.Type_Info_Named).base.variant.(runtime.Type_Info_Enum); @@ -526,13 +526,13 @@ request_initialize :: proc (task: ^common.Task) { */ if core, ok := config.collections["core"]; ok { - when ODIN_OS == "windows" { + when ODIN_OS == "windows" { append(&index.indexer.built_in_packages, path.join(strings.to_lower(core, context.temp_allocator), "runtime")); } else { append(&index.indexer.built_in_packages, path.join(core, "runtime")); } } - + log.info("Finished indexing"); } @@ -624,7 +624,7 @@ request_completion :: proc (task: ^common.Task) { } list: CompletionList; - list, ok = get_completion_list(document, completition_params.position); + list, ok = get_completion_list(document, completition_params.position, completition_params.context_); if !ok { handle_error(.InternalError, id, writer); @@ -878,7 +878,7 @@ notification_did_save :: proc (task: ^common.Task) { index.indexer.dynamic_index.collection.symbols[key] = {}; } } - + if ret := index.collect_symbols(&index.indexer.dynamic_index.collection, file, uri.uri); ret != .None { log.errorf("failed to collect symbols on save %v", ret); } diff --git a/src/server/types.odin b/src/server/types.odin index 0efbd39..e2740f7 100644 --- a/src/server/types.odin +++ b/src/server/types.odin @@ -96,6 +96,10 @@ CompletionOptions :: struct { triggerCharacters: []string, } +CompletionContext :: struct { + triggerCharacter: string, +} + SaveOptions :: struct { includeText: bool, } @@ -222,6 +226,7 @@ SignatureHelpParams :: struct { CompletionParams :: struct { textDocument: TextDocumentIdentifier, position: common.Position, + context_: CompletionContext, } CompletionItemKind :: enum { diff --git a/src/server/unmarshal.odin b/src/server/unmarshal.odin index ba37497..1776b07 100644 --- a/src/server/unmarshal.odin +++ b/src/server/unmarshal.odin @@ -32,9 +32,19 @@ unmarshal :: proc(json_value: json.Value, v: any, allocator: mem.Allocator) -> j case Type_Info_Struct: for field, i in variant.names { a := any {rawptr(uintptr(v.data) + uintptr(variant.offsets[i])), variant.types[i].id}; - if ret := unmarshal(j[field], a, allocator); ret != .None { - return ret; + + //TEMP most likely have to rewrite the entire unmarshal using tags instead, because i sometimes have to support names like 'context', which can't be written like that + if field[len(field)-1] == '_' { + if ret := unmarshal(j[field[:len(field)-1]], a, allocator); ret != .None { + return ret; + } + } else { + if ret := unmarshal(j[field], a, allocator); ret != .None { + return ret; + } } + + } case Type_Info_Union: |