diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/analysis/analysis.odin | 32 | ||||
| -rw-r--r-- | src/common/types.odin | 1 | ||||
| -rw-r--r-- | src/index/symbol.odin | 7 | ||||
| -rw-r--r-- | src/server/caches.odin | 6 | ||||
| -rw-r--r-- | src/server/completion.odin | 68 | ||||
| -rw-r--r-- | src/server/documents.odin | 4 | ||||
| -rw-r--r-- | src/server/hover.odin | 10 | ||||
| -rw-r--r-- | src/server/inlay_hints.odin | 6 | ||||
| -rw-r--r-- | src/server/on_typing.odin | 1 | ||||
| -rw-r--r-- | src/server/rename.odin | 71 | ||||
| -rw-r--r-- | src/server/requests.odin | 47 | ||||
| -rw-r--r-- | src/server/semantic_tokens.odin | 16 | ||||
| -rw-r--r-- | src/server/types.odin | 38 |
13 files changed, 271 insertions, 36 deletions
diff --git a/src/analysis/analysis.odin b/src/analysis/analysis.odin index 82f0337..6f0a288 100644 --- a/src/analysis/analysis.odin +++ b/src/analysis/analysis.odin @@ -1207,6 +1207,7 @@ resolve_type_identifier :: proc(ast_context: ^AstContext, node: ast.Ident) -> (i } if is_variable, ok := ast_context.variables[node.name]; ok && is_variable { + //return_symbol.name = node.name return_symbol.type = .Variable } @@ -2417,17 +2418,17 @@ clear_locals :: proc(ast_context: ^AstContext) { clear(&ast_context.usings) } -resolve_entire_file :: proc(document: ^common.Document, allocator := context.allocator) -> map[uintptr]index.Symbol { +resolve_entire_file :: proc(document: ^common.Document, allocator := context.allocator) -> map[uintptr]index.SymbolAndNode { ast_context := make_ast_context(document.ast, document.imports, document.package_name, document.uri.uri, allocator) get_globals(document.ast, &ast_context) ast_context.current_package = ast_context.document_package - symbols := make(map[uintptr]index.Symbol, 10000, allocator) + symbols := make(map[uintptr]index.SymbolAndNode, 10000, allocator) - for k, v in ast_context.globals { - resolve_entire_decl(&ast_context, v.expr, &symbols, allocator) + for decl in document.ast.decls { + resolve_entire_decl(&ast_context, decl, &symbols, allocator) clear_local_group(&ast_context, 0) add_local_group(&ast_context, 0) } @@ -2435,7 +2436,7 @@ resolve_entire_file :: proc(document: ^common.Document, allocator := context.all return symbols } -resolve_entire_decl :: proc(ast_context: ^AstContext, decl: ^ast.Expr, symbols: ^map[uintptr]index.Symbol, allocator := context.allocator) { +resolve_entire_decl :: proc(ast_context: ^AstContext, decl: ^ast.Node, symbols: ^map[uintptr]index.SymbolAndNode, allocator := context.allocator) { Scope :: struct { offset: int, id: int, @@ -2443,7 +2444,7 @@ resolve_entire_decl :: proc(ast_context: ^AstContext, decl: ^ast.Expr, symbols: Visit_Data :: struct { ast_context: ^AstContext, - symbols: ^map[uintptr]index.Symbol, + symbols: ^map[uintptr]index.SymbolAndNode, scopes: [dynamic]Scope, id_counter: int, } @@ -2497,16 +2498,25 @@ resolve_entire_decl :: proc(ast_context: ^AstContext, decl: ^ast.Expr, symbols: get_locals_stmt(ast_context.file, cast(^ast.Stmt)node, ast_context, &position_context) case ^ast.Ident: if symbol, ok := resolve_type_identifier(ast_context, v^); ok { - data.symbols[cast(uintptr)node] = symbol - } + data.symbols[cast(uintptr)node] = index.SymbolAndNode { + node = v, + symbol = symbol, + } + } case ^ast.Selector_Expr: if symbol, ok := resolve_type_expression(ast_context, &v.node); ok { - data.symbols[cast(uintptr)node] = symbol + data.symbols[cast(uintptr)node] = index.SymbolAndNode { + node = v, + symbol = symbol, + } } case ^ast.Call_Expr: if symbol, ok := resolve_type_expression(ast_context, &v.node); ok { - data.symbols[cast(uintptr)node] = symbol - } + data.symbols[cast(uintptr)node] = index.SymbolAndNode { + node = v, + symbol = symbol, + } + } } #partial switch v in node.derived { diff --git a/src/common/types.odin b/src/common/types.odin index c99bc3c..51affaa 100644 --- a/src/common/types.odin +++ b/src/common/types.odin @@ -39,6 +39,7 @@ Document :: struct { package_name: string, allocator: ^Scratch_Allocator, //because parser does not support freeing I use arena allocators for each document operating_on: int, //atomic + version: Maybe(int), } parser_warning_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) { diff --git a/src/index/symbol.odin b/src/index/symbol.odin index d5bba05..312264a 100644 --- a/src/index/symbol.odin +++ b/src/index/symbol.odin @@ -10,6 +10,11 @@ import "core:slice" import "shared:common" +SymbolAndNode :: struct { + symbol: Symbol, + node: ^ast.Node, +} + SymbolStructValue :: struct { names: []string, types: []^ast.Expr, @@ -118,7 +123,7 @@ Symbol :: struct { signature: string, //type signature type: SymbolType, value: SymbolValue, - references: []common.Location, //all the places in the project that it's being referenced + references: []common.Range, //all the places in the project that it's being referenced pointers: int, //how many `^` are applied to the symbol flags: SymbolFlags, } diff --git a/src/server/caches.odin b/src/server/caches.odin index 2bb4b7f..cc993c3 100644 --- a/src/server/caches.odin +++ b/src/server/caches.odin @@ -6,16 +6,18 @@ import "shared:common" //Used in semantic tokens and inlay hints to handle the entire file being resolved. FileResolveCache :: struct { - files: map[string]map[uintptr]index.Symbol, + files: map[string]map[uintptr]index.SymbolAndNode, } file_resolve_cache: FileResolveCache -resolve_entire_file :: proc(document: ^common.Document) { +resolve_entire_file :: proc(document: ^common.Document) -> map[uintptr]index.SymbolAndNode{ if document.uri.uri not_in file_resolve_cache.files { file_resolve_cache.files[document.uri.uri] = analysis.resolve_entire_file( document, common.scratch_allocator(document.allocator), ) } + + return file_resolve_cache.files[document.uri.uri]; } diff --git a/src/server/completion.odin b/src/server/completion.odin index 6e67e35..4f9d4d0 100644 --- a/src/server/completion.odin +++ b/src/server/completion.odin @@ -460,6 +460,9 @@ get_selector_completion :: proc(ast_context: ^analysis.AstContext, position_cont case index.SymbolDynamicArrayValue: list.isIncomplete = false append_magic_dynamic_array_completion(position_context, selector, &items) + case index.SymbolMapValue: + list.isIncomplete = false + append_magic_map_completion(position_context, selector, &items) } list.items = items[:] @@ -1229,6 +1232,50 @@ get_range_from_selection_start_to_dot :: proc(position_context: ^analysis.Docume return {}, false } +append_magic_map_completion :: proc(position_context: ^analysis.DocumentPositionContext, symbol: index.Symbol, items: ^[dynamic]CompletionItem) { + range, ok := get_range_from_selection_start_to_dot(position_context) + + if !ok { + return + } + + remove_range := common.Range { + start = range.start, + end = range.end, + } + + remove_edit := TextEdit { + range = remove_range, + newText = "", + } + + additionalTextEdits := make([]TextEdit, 1, context.temp_allocator) + additionalTextEdits[0] = remove_edit + + //for + { + item := CompletionItem { + label = "for", + kind = .Snippet, + detail = "for", + additionalTextEdits = additionalTextEdits, + textEdit = TextEdit { + newText = fmt.tprintf("for k, v in %v {{\n\t$0 \n}}", symbol.name), + range = { + start = range.end, + end = range.end, + }, + }, + insertTextFormat = .Snippet, + InsertTextMode = .adjustIndentation, + } + + append(items, item) + } + + +} + append_magic_dynamic_array_completion :: proc(position_context: ^analysis.DocumentPositionContext, symbol: index.Symbol, items: ^[dynamic]CompletionItem) { range, ok := get_range_from_selection_start_to_dot(position_context) @@ -1269,6 +1316,27 @@ append_magic_dynamic_array_completion :: proc(position_context: ^analysis.Docume append(items, item) } + + //for + { + item := CompletionItem { + label = "for", + kind = .Snippet, + detail = "for", + additionalTextEdits = additionalTextEdits, + textEdit = TextEdit { + newText = fmt.tprintf("for i in %v {{\n\t$0 \n}}", symbol.name), + range = { + start = range.end, + end = range.end, + }, + }, + insertTextFormat = .Snippet, + InsertTextMode = .adjustIndentation, + } + + append(items, item) + } } diff --git a/src/server/documents.odin b/src/server/documents.odin index 3ac8cdc..0c18249 100644 --- a/src/server/documents.odin +++ b/src/server/documents.odin @@ -133,7 +133,7 @@ document_open :: proc(uri_string: string, text: string, config: ^common.Config, /* Function that applies changes to the given document through incremental syncronization */ -document_apply_changes :: proc(uri_string: string, changes: [dynamic]TextDocumentContentChangeEvent, config: ^common.Config, writer: ^Writer) -> common.Error { +document_apply_changes :: proc(uri_string: string, changes: [dynamic]TextDocumentContentChangeEvent, version: Maybe(int), config: ^common.Config, writer: ^Writer) -> common.Error { uri, parsed_ok := common.parse_uri(uri_string, context.temp_allocator) if !parsed_ok { @@ -142,6 +142,8 @@ document_apply_changes :: proc(uri_string: string, changes: [dynamic]TextDocumen document := &document_storage.documents[uri.path] + document.version = version + if !document.client_owned { log.errorf("Client called change on an document not opened: %v ", document.uri.path) return .InvalidRequest diff --git a/src/server/hover.odin b/src/server/hover.odin index a5c3b5e..4286ee8 100644 --- a/src/server/hover.odin +++ b/src/server/hover.odin @@ -127,10 +127,10 @@ get_hover_information :: proc(document: ^common.Document, position: common.Posit for name, i in v.names { if strings.compare(name, field) == 0 { if symbol, ok := resolve_type_expression(&ast_context, v.types[i]); ok { - symbol.name = name //TODO refractor - never set symbol name after creation - change writer_hover_content - symbol.pkg = selector.name + symbol.name = name //TODO refractor - never set symbol name after creation - change writer_hover_content + symbol.pkg = selector.name symbol.signature = common.node_to_string(v.types[i]) - hover.contents = write_hover_content(&ast_context, symbol) + hover.contents = write_hover_content(&ast_context, symbol) return hover, true } } @@ -147,8 +147,8 @@ get_hover_information :: proc(document: ^common.Document, position: common.Posit } } } else if position_context.identifier != nil { - ast_context.use_locals = true - ast_context.use_globals = true + ast_context.use_locals = true + ast_context.use_globals = true ast_context.current_package = ast_context.document_package ident := position_context.identifier.derived.(^ast.Ident)^ diff --git a/src/server/inlay_hints.odin b/src/server/inlay_hints.odin index ae3d9a0..a5be9c2 100644 --- a/src/server/inlay_hints.odin +++ b/src/server/inlay_hints.odin @@ -8,7 +8,7 @@ import "shared:analysis" import "shared:index" //document -get_inlay_hints :: proc(document: ^common.Document, symbols: map[uintptr]index.Symbol) -> ([]InlayHint, bool) { +get_inlay_hints :: proc(document: ^common.Document, symbols: map[uintptr]index.SymbolAndNode) -> ([]InlayHint, bool) { using analysis hints := make([dynamic]InlayHint, context.temp_allocator) @@ -57,8 +57,8 @@ get_inlay_hints :: proc(document: ^common.Document, symbols: map[uintptr]index.S } } - if symbol, ok := symbols[cast(uintptr)node_call]; ok { - if symbol_call, ok := symbol.value.(index.SymbolProcedureValue); ok { + if symbol_and_node, ok := symbols[cast(uintptr)node_call]; ok { + if symbol_call, ok := symbol_and_node.symbol.value.(index.SymbolProcedureValue); ok { for arg in symbol_call.arg_types { for name in arg.names { if symbol_arg_count >= len(call.args) { diff --git a/src/server/on_typing.odin b/src/server/on_typing.odin new file mode 100644 index 0000000..abb4e43 --- /dev/null +++ b/src/server/on_typing.odin @@ -0,0 +1 @@ +package server diff --git a/src/server/rename.odin b/src/server/rename.odin new file mode 100644 index 0000000..b7d39ef --- /dev/null +++ b/src/server/rename.odin @@ -0,0 +1,71 @@ +package server + +import "shared:common" +import "shared:analysis" + +import "core:log" +import "core:odin/ast" + +get_rename :: proc(document: ^common.Document, new_text: string, position: common.Position) -> (WorkspaceEdit, bool) { + using analysis + + workspace: WorkspaceEdit + + document_changes := make([dynamic]TextDocumentEdit, context.temp_allocator) + + edits := make([dynamic]TextEdit, context.temp_allocator) + + symbol_and_nodes := resolve_entire_file(document) + + ast_context := make_ast_context(document.ast, document.imports, document.package_name, document.uri.uri) + + position_context, ok := get_document_position_context(document, position, .Hover) + + get_globals(document.ast, &ast_context) + + if position_context.function != nil { + get_locals(document.ast, position_context.function, &ast_context, &position_context) + } + + if position_context.identifier != nil { + ast_context.use_locals = true + ast_context.use_globals = true + ast_context.current_package = ast_context.document_package + + ident := position_context.identifier.derived.(^ast.Ident)^ + + if resolved, ok := resolve_type_identifier(&ast_context, ident); ok { + for k, v in symbol_and_nodes { + if ident2, ok := v.node.derived.(^ast.Ident); ok { + log.error(ident2) + } + if ident2, ok := v.node.derived.(^ast.Ident); ok && resolved.pkg == v.symbol.pkg && ident2.name == ident.name { + edit := TextEdit { + newText = new_text, + range = common.get_token_range(v.node^, position_context.file.src), + } + append(&edits, edit) + } + } + + + + + } + + } + + document_change := TextDocumentEdit { + edits = edits[:], + textDocument = { + uri = document.uri.uri, + version = document.version, + }, + } + + append(&document_changes, document_change) + + workspace.documentChanges = document_changes[:] + + return workspace, true +}
\ No newline at end of file diff --git a/src/server/requests.odin b/src/server/requests.odin index 0c63e46..38e390f 100644 --- a/src/server/requests.odin +++ b/src/server/requests.odin @@ -261,6 +261,7 @@ call_map : map [string] proc(json.Value, RequestId, ^common.Config, ^Writer) -> "textDocument/formatting" = request_format_document, "odin/inlayHints" = request_inlay_hint, "textDocument/documentLink" = request_document_links, + "textDocument/rename" = request_rename, } notification_map: map [string] bool = { @@ -502,6 +503,7 @@ request_initialize :: proc (params: json.Value, id: RequestId, config: ^common.C includeText = true, }, }, + renameProvider = false, definitionProvider = true, completionProvider = CompletionOptions { resolveProvider = false, @@ -665,9 +667,14 @@ request_signature_help :: proc (params: json.Value, id: RequestId, config: ^comm return .InternalError } - response := make_response_message(params = help, id = id) + if len(help.signatures) == 0 { + response := make_response_message(params = nil, id = id) + send_response(response, writer) + } else { + response := make_response_message(params = help, id = id) + send_response(response, writer) + } - send_response(response, writer) return .None } @@ -745,7 +752,7 @@ notification_did_change :: proc (params: json.Value, id: RequestId, config: ^com return .ParseError } - document_apply_changes(change_params.textDocument.uri, change_params.contentChanges, config, writer) + document_apply_changes(change_params.textDocument.uri, change_params.contentChanges, change_params.textDocument.version, config, writer) return .None } @@ -1050,4 +1057,38 @@ request_document_links :: proc (params: json.Value, id: RequestId, config: ^comm send_response(response, writer) return .None +} + +request_rename :: proc (params: json.Value, id: RequestId, config: ^common.Config, writer: ^Writer) -> common.Error { + params_object, ok := params.(json.Object) + + if !ok { + return .ParseError + } + + rename_param: RenameParams + + if unmarshal(params, rename_param, context.temp_allocator) != .None { + return .ParseError + } + + document := document_get(rename_param.textDocument.uri) + + if document == nil { + return .InternalError + } + + workspace_edit: WorkspaceEdit + + workspace_edit, ok = get_rename(document, rename_param.newName, rename_param.position) + + if !ok { + return .InternalError + } + + response := make_response_message(params = workspace_edit, id = id) + + send_response(response, writer) + + return .None }
\ No newline at end of file diff --git a/src/server/semantic_tokens.odin b/src/server/semantic_tokens.odin index 98f534b..5ae011d 100644 --- a/src/server/semantic_tokens.odin +++ b/src/server/semantic_tokens.odin @@ -78,7 +78,7 @@ SemanticTokens :: struct { SemanticTokenBuilder :: struct { current_start: int, tokens: [dynamic]u32, - symbols: map[uintptr]index.Symbol, + symbols: map[uintptr]index.SymbolAndNode, selector: bool, } @@ -94,7 +94,7 @@ get_tokens :: proc(builder: SemanticTokenBuilder) -> SemanticTokens { } } -get_semantic_tokens :: proc(document: ^common.Document, range: common.Range, symbols: map[uintptr]index.Symbol) -> SemanticTokens { +get_semantic_tokens :: proc(document: ^common.Document, range: common.Range, symbols: map[uintptr]index.SymbolAndNode) -> SemanticTokens { using analysis builder := make_token_builder() @@ -172,13 +172,13 @@ visit_node :: proc(node: ^ast.Node, builder: ^SemanticTokenBuilder, ast_context: write_semantic_string(builder, node.pos, "..", ast_context.file.src, .Operator, .None) visit(n.expr, builder, ast_context) case ^Ident: - if symbol, ok := builder.symbols[cast(uintptr)node]; ok { - if symbol.type == .Variable { + if symbol_and_node, ok := builder.symbols[cast(uintptr)node]; ok { + if symbol_and_node.symbol.type == .Variable { write_semantic_node(builder, node, ast_context.file.src, .Variable, .None) return } - #partial switch v in symbol.value { + #partial switch v in symbol_and_node.symbol.value { case index.SymbolPackageValue: write_semantic_node(builder, node, ast_context.file.src, .Namespace, .None) case index.SymbolStructValue: @@ -506,11 +506,11 @@ visit_selector :: proc(selector: ^ast.Selector_Expr, builder: ^SemanticTokenBuil builder.selector = true } - if symbol, ok := builder.symbols[cast(uintptr)selector]; ok { - if symbol.type == .Variable { + if symbol_and_node, ok := builder.symbols[cast(uintptr)selector]; ok { + if symbol_and_node.symbol.type == .Variable { write_semantic_node(builder, selector.field, ast_context.file.src, .Method, .None) } - #partial switch v in symbol.value { + #partial switch v in symbol_and_node.symbol.value { case index.SymbolPackageValue: write_semantic_node(builder, selector.field, ast_context.file.src, .Namespace, .None) case index.SymbolStructValue: diff --git a/src/server/types.odin b/src/server/types.odin index 1440472..54168d7 100644 --- a/src/server/types.odin +++ b/src/server/types.odin @@ -28,6 +28,7 @@ ResponseParams :: union { []TextEdit, []InlayHint, []DocumentLink, + WorkspaceEdit, } ResponseMessage :: struct { @@ -94,6 +95,7 @@ ServerCapabilities :: struct { hoverProvider: bool, documentFormattingProvider: bool, inlayHintsProvider: bool, + renameProvider: bool, documentLinkProvider: DocumentLinkOptions, } @@ -170,7 +172,8 @@ Version :: union { } VersionedTextDocumentIdentifier :: struct { - uri: string, + uri: string, + version: int, } TextDocumentIdentifier :: struct { @@ -416,4 +419,35 @@ DocumentLink :: struct { DocumentLinkOptions :: struct { resolveProvider: bool, -}
\ No newline at end of file +} + +PrepareSupportDefaultBehavior :: enum { + Identifier = 1, +} + +RenameClientCapabilities :: struct { + prepareSupport: bool, + prepareSupportDefaultBehavior: PrepareSupportDefaultBehavior, + honorsChangeAnnotations: bool, +} + +RenameParams :: struct { + newName: string, + textDocument: TextDocumentIdentifier, + position: common.Position, +} + +OptionalVersionedTextDocumentIdentifier :: struct { + uri: string, + version: Maybe(int), +} + +TextDocumentEdit :: struct { + textDocument: OptionalVersionedTextDocumentIdentifier, + edits: []TextEdit, +} + +WorkspaceEdit :: struct { + documentChanges: []TextDocumentEdit, +} + |