aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorDanielGavin <danielgavin5@hotmail.com>2021-03-31 20:59:02 +0200
committerDanielGavin <danielgavin5@hotmail.com>2021-03-31 20:59:02 +0200
commita3bf952b19d210b9578c40dffe5ec48c76cca49c (patch)
tree203da390144ab4b9b91e696db1bd45ac65c0f520 /src/server
parent676f27e656073ab40ccbb19bb3470b8801e421e6 (diff)
start work on package completion
Diffstat (limited to 'src/server')
-rw-r--r--src/server/analysis.odin16
-rw-r--r--src/server/completion.odin98
-rw-r--r--src/server/requests.odin14
-rw-r--r--src/server/types.odin5
-rw-r--r--src/server/unmarshal.odin14
5 files changed, 133 insertions, 14 deletions
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: