aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDanielGavin <danielgavin5@hotmail.com>2020-11-07 18:25:28 +0100
committerDanielGavin <danielgavin5@hotmail.com>2020-11-07 18:25:28 +0100
commit5f5e855af2473bf5ff12ecac296dad37b5d923ac (patch)
treeff99477b52673ce33d69ba2f3b21c9990db511a0 /src
parent6ec424ed8d34cf8a5f51e277a20429741b33ee96 (diff)
started on fuzzy searching on the naive indexer.
Diffstat (limited to 'src')
-rw-r--r--src/common/fuzzy.odin24
-rw-r--r--src/index/build.odin1
-rw-r--r--src/index/indexer.odin10
-rw-r--r--src/index/memory_index.odin38
-rw-r--r--src/index/symbol.odin5
-rw-r--r--src/server/analysis.odin79
-rw-r--r--src/server/requests.odin49
-rw-r--r--src/server/types.odin54
8 files changed, 238 insertions, 22 deletions
diff --git a/src/common/fuzzy.odin b/src/common/fuzzy.odin
new file mode 100644
index 0000000..4a03c45
--- /dev/null
+++ b/src/common/fuzzy.odin
@@ -0,0 +1,24 @@
+package common
+
+
+FuzzyMatcher :: struct {
+ pattern: string,
+};
+
+make_fuzzy_matcher :: proc (pattern: string) -> FuzzyMatcher {
+ return FuzzyMatcher {
+ pattern = pattern
+ };
+}
+
+fuzzy_match :: proc (matcher: FuzzyMatcher, match: string) -> f64 {
+
+ //temp just look at the beginning on the character - will need to learn about fuzzy matching first.
+
+ if matcher.pattern[0] == match[0] {
+ return 1.0;
+ }
+
+
+ return 0.0;
+}
diff --git a/src/index/build.odin b/src/index/build.odin
index d52cbca..3d96405 100644
--- a/src/index/build.odin
+++ b/src/index/build.odin
@@ -40,7 +40,6 @@ build_static_index :: proc(allocator := context.allocator, config: ^common.Confi
data, ok := os.read_entire_file(info.fullpath, context.allocator);
if !ok {
- fmt.println("failed to read");
return 1, false;
}
diff --git a/src/index/indexer.odin b/src/index/indexer.odin
index 46a2192..b19cd7e 100644
--- a/src/index/indexer.odin
+++ b/src/index/indexer.odin
@@ -31,10 +31,6 @@ import "core:strings"
This index is first searched and if nothing is found look in the static index.
- interface ideas:
-
- index_search_fuzzy(symbol: string, scope: [] string) -> [] SymbolResult
-
TODO(Daniel, Look into data structure for fuzzy searching)
*/
@@ -51,4 +47,8 @@ lookup :: proc(id: string) -> (Symbol, bool) {
return memory_index_lookup(&indexer.static_index, id);
}
-//indexer_fuzzy_search :: proc(name: string, scope: [] string, )
+
+fuzzy_search :: proc(name: string, scope: [] string) -> ([] Symbol, bool) {
+ return memory_index_fuzzy_search(&indexer.static_index, name, scope);
+}
+
diff --git a/src/index/memory_index.odin b/src/index/memory_index.odin
index c5ce416..4a9ade1 100644
--- a/src/index/memory_index.odin
+++ b/src/index/memory_index.odin
@@ -1,12 +1,16 @@
package index
import "core:hash"
+import "core:strings"
+import "core:fmt"
+
+import "shared:common"
/*
This is a in memory index designed for the dynamic indexing of symbols and files.
Designed for few files and should be fast at rebuilding.
- Right now the implementation is quite naive.
+ Right now the implementation is extremely naive.
*/
MemoryIndex :: struct {
collection: SymbolCollection,
@@ -26,4 +30,36 @@ memory_index_lookup :: proc(index: ^MemoryIndex, id: string) -> (Symbol, bool) {
hashed := hash.murmur64(transmute([]u8)id);
return index.collection.symbols[hashed];
+}
+
+memory_index_fuzzy_search :: proc(index: ^MemoryIndex, name: string, scope: [] string) -> ([] Symbol, bool) {
+
+ symbols := make([dynamic] Symbol, 0, context.temp_allocator);
+
+ fuzzy_matcher := common.make_fuzzy_matcher(name);
+
+ for _, symbol in index.collection.symbols {
+
+ if !exists_in_scope(symbol.scope, scope) {
+ continue;
+ }
+
+ if common.fuzzy_match(fuzzy_matcher, symbol.name) > 0.5 {
+ append(&symbols, symbol);
+ }
+
+ }
+
+ return symbols[:], true;
+}
+
+exists_in_scope :: proc(symbol_scope: string, scope: [] string) -> bool {
+
+ for s in scope {
+ if strings.compare(symbol_scope, s) == 0 {
+ return true;
+ }
+ }
+
+ return false;
} \ No newline at end of file
diff --git a/src/index/symbol.odin b/src/index/symbol.odin
index 2cbd58c..ecec5f8 100644
--- a/src/index/symbol.odin
+++ b/src/index/symbol.odin
@@ -4,6 +4,7 @@ import "core:odin/ast"
import "core:hash"
import "core:strings"
import "core:mem"
+import "core:fmt"
import "shared:common"
@@ -11,6 +12,8 @@ Symbol :: struct {
id: u64,
range: common.Range,
uri: string,
+ scope: string,
+ name: string,
};
SymbolCollection :: struct {
@@ -42,6 +45,8 @@ collect_symbols :: proc(collection: ^SymbolCollection, file: ast.File, uri: stri
symbol: Symbol;
symbol.range = common.get_token_range(proc_lit, file.src);
+ symbol.name = strings.clone(name);
+ symbol.scope = strings.clone(file.pkg_name); //have this use unique strings to save space
uri_id := hash.murmur64(transmute([]u8)uri);
diff --git a/src/server/analysis.odin b/src/server/analysis.odin
index e88532f..8027e6d 100644
--- a/src/server/analysis.odin
+++ b/src/server/analysis.odin
@@ -13,13 +13,17 @@ import "shared:index"
-DocumentPositionContextDottedValue :: struct {
+DocumentPositionContextVariableDotVariableValue :: struct {
prefix: string,
postfix: string,
};
DocumentPositionContextGlobalValue :: struct {
+ name: string,
+};
+DocumentPositionContextVariableDotValue :: struct {
+ prefix: string,
};
DocumentPositionContextUnknownValue :: struct {
@@ -27,9 +31,10 @@ DocumentPositionContextUnknownValue :: struct {
}
DocumentPositionContextValue :: union {
- DocumentPositionContextDottedValue,
+ DocumentPositionContextVariableDotValue,
DocumentPositionContextGlobalValue,
- DocumentPositionContextUnknownValue
+ DocumentPositionContextUnknownValue,
+ DocumentPositionContextVariableDotVariableValue,
};
DocumentPositionContext :: struct {
@@ -69,6 +74,8 @@ get_document_position_context :: proc(document: ^Document, position: common.Posi
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
*/
@@ -84,11 +91,13 @@ get_document_position_context :: proc(document: ^Document, position: common.Posi
struct_or_package = last_token;
}
case .Ident:
+ last_label = true;
case .EOF:
+ last_label = false;
break;
case:
struct_or_package_dotted = false;
-
+ last_label = false;
}
if current_token.pos.offset+len(current_token.text) >= absolute_position {
@@ -101,21 +110,35 @@ get_document_position_context :: proc(document: ^Document, position: common.Posi
#partial switch current_token.kind {
case .Ident:
if struct_or_package_dotted {
- position_context.value = DocumentPositionContextDottedValue {
+ 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 {
+ };
}
+
case:
position_context.value = DocumentPositionContextUnknownValue {
};
}
- //fmt.println(position_context);
+ log.info(position_context);
return position_context, true;
}
@@ -123,11 +146,8 @@ get_document_position_context :: proc(document: ^Document, position: common.Posi
get_definition_location :: proc(document: ^Document, position: common.Position) -> (common.Location, bool) {
-
location: common.Location;
-
-
position_context, ok := get_document_position_context(document, position);
if !ok {
@@ -137,14 +157,14 @@ get_definition_location :: proc(document: ^Document, position: common.Position)
symbol: index.Symbol;
#partial switch v in position_context.value {
- case DocumentPositionContextDottedValue:
+ 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:
return location, false;
}
- //fmt.println(indexer.symbol_table);
-
if !ok {
return location, false;
}
@@ -156,3 +176,38 @@ get_definition_location :: proc(document: ^Document, position: common.Position)
return location, true;
}
+get_completion_list :: proc(document: ^Document, position: common.Position) -> (CompletionList, bool) {
+
+ list: CompletionList;
+
+
+ position_context, ok := get_document_position_context(document, position);
+ symbols: [] index.Symbol;
+ empty_dot := false;
+
+
+ #partial switch v in position_context.value {
+ case DocumentPositionContextVariableDotVariableValue:
+ symbols, ok = index.fuzzy_search(v.postfix, {v.prefix});
+ case DocumentPositionContextVariableDotValue:
+ empty_dot = true;
+ }
+
+ if empty_dot {
+ list.isIncomplete = true;
+ return list, true;
+ }
+
+ if !ok {
+ return list, false;
+ }
+
+ list.items = make([] CompletionItem, len(symbols), context.temp_allocator);
+
+ for symbol, i in symbols {
+ list.items[i].label = symbol.name;
+ list.items[i].kind = .Function;
+ }
+
+ return list, true;
+} \ No newline at end of file
diff --git a/src/server/requests.odin b/src/server/requests.odin
index cd8e734..587e444 100644
--- a/src/server/requests.odin
+++ b/src/server/requests.odin
@@ -167,7 +167,8 @@ handle_request :: proc(request: json.Value, config: ^common.Config, writer: ^Wri
"textDocument/didChange" = notification_did_change,
"textDocument/didClose" = notification_did_close,
"textDocument/didSave" = notification_did_save,
- "textDocument/definition" = request_definition };
+ "textDocument/definition" = request_definition,
+ "textDocument/completion" = request_completion};
fn: proc(json.Value, RequestId, ^common.Config, ^Writer) -> common.Error;
fn, ok = call_map[method];
@@ -225,11 +226,17 @@ request_initialize :: proc(params: json.Value, id: RequestId, config: ^common.Co
}
}
+ triggerCharacters := [] string { "." };
+
response := make_response_message(
params = ResponseInitializeParams {
capabilities = ServerCapabilities {
textDocumentSync = 2, //incremental
definitionProvider = true,
+ completionProvider = CompletionOptions {
+ resolveProvider = false,
+ triggerCharacters = triggerCharacters,
+ },
},
},
id = id,
@@ -295,6 +302,46 @@ request_definition :: proc(params: json.Value, id: RequestId, config: ^common.Co
return .None;
}
+
+request_completion :: proc(params: json.Value, id: RequestId, config: ^common.Config, writer: ^Writer) -> common.Error {
+
+ params_object, ok := params.value.(json.Object);
+
+ if !ok {
+ return .ParseError;
+ }
+
+ completition_params: CompletionParams;
+
+ if unmarshal(params, completition_params, context.temp_allocator) != .None {
+ return .ParseError;
+ }
+
+
+ document := document_get(completition_params.textDocument.uri);
+
+ if document == nil {
+ return .InternalError;
+ }
+
+ list: CompletionList;
+ list, ok = get_completion_list(document, completition_params.position);
+
+ if !ok {
+ return .InternalError;
+ }
+
+ response := make_response_message(
+ params = list,
+ 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;
return .None;
diff --git a/src/server/types.odin b/src/server/types.odin
index 262f464..61f2cba 100644
--- a/src/server/types.odin
+++ b/src/server/types.odin
@@ -19,6 +19,7 @@ ResponseParams :: union {
ResponseInitializeParams,
rawptr,
common.Location,
+ CompletionList,
};
ResponseMessage :: struct {
@@ -78,10 +79,12 @@ MarkupKind :: enum {
ServerCapabilities :: struct {
textDocumentSync: int,
definitionProvider: bool,
+ completionProvider: CompletionOptions,
};
-CompletionClientCapabilities :: struct {
-
+CompletionOptions :: struct {
+ resolveProvider: bool,
+ triggerCharacters: [] string,
};
HoverClientCapabilities :: struct {
@@ -94,6 +97,10 @@ TextDocumentClientCapabilities :: struct {
hover: HoverClientCapabilities,
};
+CompletionClientCapabilities :: struct {
+
+};
+
ClientCapabilities :: struct {
textDocument: TextDocumentClientCapabilities,
};
@@ -151,4 +158,47 @@ DidCloseTextDocumentParams :: struct {
TextDocumentPositionParams :: struct {
textDocument: TextDocumentIdentifier,
position: common.Position,
+};
+
+CompletionParams :: struct {
+ textDocument: TextDocumentIdentifier,
+ position: common.Position,
+};
+
+CompletionItemKind :: enum {
+ Text = 1,
+ Method = 2,
+ Function = 3,
+ Constructor = 4,
+ Field = 5,
+ Variable = 6,
+ Class = 7,
+ Interface = 8,
+ Module = 9,
+ Property = 10,
+ Unit = 11,
+ Value = 12,
+ Enum = 13,
+ Keyword = 14,
+ Snippet = 15,
+ Color = 16,
+ File = 17,
+ Reference = 18,
+ Folder = 19,
+ EnumMember = 20,
+ Constant = 21,
+ Struct = 22,
+ Event = 23,
+ Operator = 24,
+ TypeParameter = 25,
+};
+
+CompletionItem :: struct {
+ label: string,
+ kind: CompletionItemKind,
+};
+
+CompletionList :: struct {
+ isIncomplete: bool,
+ items: [] CompletionItem,
}; \ No newline at end of file