diff options
| author | DanielGavin <danielgavin5@hotmail.com> | 2020-11-07 18:25:28 +0100 |
|---|---|---|
| committer | DanielGavin <danielgavin5@hotmail.com> | 2020-11-07 18:25:28 +0100 |
| commit | 5f5e855af2473bf5ff12ecac296dad37b5d923ac (patch) | |
| tree | ff99477b52673ce33d69ba2f3b21c9990db511a0 /src | |
| parent | 6ec424ed8d34cf8a5f51e277a20429741b33ee96 (diff) | |
started on fuzzy searching on the naive indexer.
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/fuzzy.odin | 24 | ||||
| -rw-r--r-- | src/index/build.odin | 1 | ||||
| -rw-r--r-- | src/index/indexer.odin | 10 | ||||
| -rw-r--r-- | src/index/memory_index.odin | 38 | ||||
| -rw-r--r-- | src/index/symbol.odin | 5 | ||||
| -rw-r--r-- | src/server/analysis.odin | 79 | ||||
| -rw-r--r-- | src/server/requests.odin | 49 | ||||
| -rw-r--r-- | src/server/types.odin | 54 |
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 |