diff --git a/C/watl.v0.msvc.c b/C/watl.v0.msvc.c index 67c2f58..327ce63 100644 --- a/C/watl.v0.msvc.c +++ b/C/watl.v0.msvc.c @@ -1680,7 +1680,7 @@ Str8 str8__fmt(Str8 fmt_template, Slice_A2_Str8* entries) { inline void str8cache__init(Str8Cache* cache, Opts_str8cache_init* opts) { assert(cache != nullptr); - assert(opts != nullptr); + assert(opts != nullptr); assert(opts->str_reserve.proc != nullptr); assert(opts->cell_reserve.proc != nullptr); assert(opts->tbl_backing.proc != nullptr); @@ -1783,7 +1783,9 @@ void str8gen_append_str8(Str8Gen* gen, Str8 str){ slice_assert(result); Slice_Byte to_copy = { result.ptr + gen->len, result.len - gen->len }; slice_copy(to_copy, slice_byte(str)); - gen->ptr = cast(UTF8*, result.ptr); gen->len = result.len; + gen->ptr = cast(UTF8*, result.ptr); + gen->len += str.len; + gen->cap = max(gen->len , gen->cap); // TODO(Ed): Arenas currently hide total capacity before growth. Problably better todo classic append to actually track this. } void str8gen__append_fmt(Str8Gen* gen, Str8 fmt_template, Slice_A2_Str8* entries){ local_persist Byte tbl_mem[kilo(32)]; FArena tbl_arena = farena_make(slice_fmem(tbl_mem)); @@ -1792,8 +1794,8 @@ void str8gen__append_fmt(Str8Gen* gen, Str8 fmt_template, Slice_A2_Str8* entries if (buffer.len < kilo(16)) { Slice_Byte result = mem_grow(gen->backing, str8gen_slice_byte(* gen), kilo(16) + gen->cap ); slice_assert(result); - gen->ptr = result.ptr; - gen->cap = result.len; + gen->ptr = result.ptr; + gen->cap += kilo(16); buffer = (Slice_Byte){ cast(Byte*, gen->ptr + gen->len), gen->cap - gen->len }; } Str8 result = str8__fmt_kt1l(gen->backing, & buffer, kt, fmt_template); diff --git a/Odin/watl.v0.odin b/Odin/watl.v0.odin index 60866d9..7d244f5 100644 --- a/Odin/watl.v0.odin +++ b/Odin/watl.v0.odin @@ -13,6 +13,7 @@ main :: proc() import "base:builtin" import "base:intrinsics" +import "base:runtime" //region Package Mappings abs :: builtin.abs @@ -20,6 +21,11 @@ min :: builtin.min max :: builtin.max clamp :: builtin.clamp +ainfo :: proc { + farena_ainfo, + varena_ainfo, + arena_ainfo, +} alloc :: proc { mem_alloc, alloc_type, @@ -109,11 +115,11 @@ memory_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) - return dst } -SliceBytes :: struct { +SliceByte :: struct { data: [^]byte, len: int } -SliceRaw :: struct ($Type: typeid) { +SliceRaw :: struct ($Type: typeid) { data: [^]Type, len: int, } @@ -125,8 +131,8 @@ slice_assert :: #force_inline proc (s: $SliceType / []$Type) { } slice_end :: #force_inline proc "contextless" (s : $SliceType / []$Type) -> ^Type { return & s[len(s) - 1] } -@(require_results) slice_to_bytes :: proc "contextless" (s: []$Type) -> []byte { return ([^]byte)(raw_data(s))[:len(s) * size_of(Type)] } -@(require_results) slice_raw :: proc "contextless" (s: []$Type) -> SliceRaw(Type) { return transmute(SliceRaw(Type)) s } +@(require_results) slice_to_bytes :: proc "contextless" (s: []$Type) -> []byte { return ([^]byte)(raw_data(s))[:len(s) * size_of(Type)] } +@(require_results) slice_raw :: proc "contextless" (s: []$Type) -> SliceRaw(Type) { return transmute(SliceRaw(Type)) s } slice_zero :: proc "contextless" (data: $SliceType / []$Type) { zero(raw_data(data), size_of(Type) * len(data)) } slice_copy :: proc "contextless" (dst, src: $SliceType / []$Type) -> int { @@ -319,7 +325,7 @@ alloc_type :: proc(ainfo: AllocatorInfo, $Type: typeid, alignment: int = MEMORY ainfo.procedure(input, & output) return transmute(^Type) raw_data(output.allocation) } -alloc_slice :: proc(ainfo: AllocatorInfo, $SliceType: []$Type, num : int) -> []Type { +alloc_slice :: proc(ainfo: AllocatorInfo, $SliceType: typeid / []$Type, num : int, alignment: int = MEMORY_ALIGNMENT_DEFAULT, no_zero: b32 = false) -> []Type { assert(ainfo.procedure != nil) input := AllocatorProc_In { data = ainfo.data, @@ -329,7 +335,7 @@ alloc_slice :: proc(ainfo: AllocatorInfo, $SliceType: []$Type, num : int) -> []T } output: AllocatorProc_Out ainfo.procedure(input, & output) - return transmute([]Type) Raw_Slice { raw_data(output.allocation), num } + return transmute([]Type) slice(raw_data(output.allocation), num) } //endregion Allocator Interface @@ -459,6 +465,7 @@ farena_allocator_proc :: proc(input: AllocatorProc_In, output: ^AllocatorProc_Ou output.save_point = farena_save(arena^) } } +farena_ainfo :: #force_inline proc "contextless" (arena : ^FArena) -> AllocatorInfo { return AllocatorInfo{farena_allocator_proc, arena} } //endregion FArena //region OS @@ -709,6 +716,7 @@ varena_allocator_proc :: proc(input: AllocatorProc_In, output: ^AllocatorProc_Ou output.save_point = varena_save(vm) } } +varena_ainfo :: #force_inline proc "contextless" (arena : ^VArena) -> AllocatorInfo { return AllocatorInfo{varena_allocator_proc, arena} } //endregion VArena //region Arena (Chained Arena) @@ -850,7 +858,7 @@ arena_allocator_proc :: proc(input: AllocatorProc_In, output: ^AllocatorProc_Out if input.op == .Grow { zero(cursor(new_alloc)[len(input.old_allocation):], input.requested_size - len(input.old_allocation)) } - output.allocation = slice_to_bytes(new_alloc) + output.allocation = new_alloc output.continuity_break = true case .Shrink: @@ -888,6 +896,7 @@ arena_allocator_proc :: proc(input: AllocatorProc_In, output: ^AllocatorProc_Out output.save_point = arena_save(arena) } } +arena_ainfo :: #force_inline proc "contextless" (arena : ^Arena) -> AllocatorInfo { return AllocatorInfo{arena_allocator_proc, arena} } //endregion Arena (Casey-Ryan Composite Arena) //region Hashing @@ -907,7 +916,7 @@ KT1L_Meta :: struct { slot_size: uintptr, kt_value_offset: uintptr, type_width: uintptr, - type_name: string, + type: typeid, } kt1l_populate_slice_a2_Slice_Byte :: proc(kt: ^[]byte, backing: AllocatorInfo, values: []byte, num_values: int, m: KT1L_Meta) { assert(kt != nil) @@ -915,14 +924,14 @@ kt1l_populate_slice_a2_Slice_Byte :: proc(kt: ^[]byte, backing: AllocatorInfo, v table_size_bytes := num_values * int(m.slot_size) kt^ = mem_alloc(backing, table_size_bytes) slice_assert(kt ^) - kt_raw : SliceBytes = transmute(SliceBytes) kt^ - for cursor in 0 ..< cast(uintptr) num_values { - slot_offset := cursor * m.slot_size // slot id + kt_raw : SliceByte = transmute(SliceByte) kt^ + for id in 0 ..< cast(uintptr) num_values { + slot_offset := id * m.slot_size // slot id slot_cursor := kt_raw.data[slot_offset:] // slots[id] type: KT1L_ slot_key := cast(^u64) slot_cursor // slots[id].key type: U64 slot_value := slice(slot_cursor[m.kt_value_offset:], m.type_width) // slots[id].value type: - a2_offset := cursor * m.type_width * 2 // a2 entry id - a2_cursor := slice_cursor(values)[a2_offset:] // a2_entries[id] type: A2_ + a2_offset := id * m.type_width * 2 // a2 entry id + a2_cursor := cursor(values)[a2_offset:] // a2_entries[id] type: A2_ a2_key := (transmute(^[]byte) a2_cursor) ^ // a2_entries[id].key type: a2_value := slice(a2_cursor[m.type_width:], m.type_width) // a2_entries[id].value type: copy(slot_value, a2_value) // slots[id].value = a2_entries[id].value @@ -932,12 +941,12 @@ kt1l_populate_slice_a2_Slice_Byte :: proc(kt: ^[]byte, backing: AllocatorInfo, v } kt1l_populate_slice_a2 :: proc($Type: typeid, kt: ^[]KT1L_Slot(Type), backing: AllocatorInfo, values: [][2]Type) { assert(kt != nil) - values_bytes := transmute([]byte) SliceBytes{data = raw_data(values), len = len(values) * size_of([2]Type)} + values_bytes := slice(transmute([^]u8) raw_data(values), len(values) * size_of([2]Type)) kt1l_populate_slice_a2_Slice_Byte(transmute(^[]byte) kt, backing, values_bytes, len(values), { slot_size = size_of(KT1L_Slot(Type)), - kt_value_offset = offset_of(KT1L_Slot(Type), KT1L_Slot(Type).value), + kt_value_offset = offset_of(KT1L_Slot(Type), value), type_width = size_of(Type), - type_name = #type_string(Type), + type = Type, }) } //endregion Key Table 1-Layer Linear (KT1L) @@ -974,7 +983,7 @@ KT1CX_ByteMeta :: struct { cell_depth: int, cell_size: int, type_width: int, - type_name: string, + type: typeid, } KT1CX_InfoMeta :: struct { cell_pool_size: int, @@ -985,7 +994,7 @@ KT1CX_InfoMeta :: struct { cell_depth: int, cell_size: int, type_width: int, - type_name: string, + type: typeid, } KT1CX_Info :: struct { backing_table: AllocatorInfo, @@ -999,7 +1008,7 @@ kt1cx_init :: proc(info: KT1CX_Info, m: KT1CX_InfoMeta, result: ^KT1CX_Byte) { assert(m.cell_pool_size >= 4 * Kilo) assert(m.table_size >= 4 * Kilo) assert(m.type_width > 0) - table_raw := transmute(SliceBytes) mem_alloc(info.backing_table, m.table_size * m.cell_size) + table_raw := transmute(SliceByte) mem_alloc(info.backing_table, m.table_size * m.cell_size) slice_assert(transmute([]byte) table_raw) result.cell_pool = mem_alloc(info.backing_cells, m.cell_pool_size * m.cell_size) slice_assert(result.cell_pool) @@ -1011,7 +1020,7 @@ kt1cx_clear :: proc(kt: KT1CX_Byte, m: KT1CX_ByteMeta) { table_len := len(kt.table) * m.cell_size for ; cell_cursor != end(kt.table); cell_cursor = cell_cursor[m.cell_size:] // for cell, cell_id in kt.table.cells { - slots := SliceBytes { cell_cursor, m.cell_depth * m.slot_size } // slots = cell.slots + slots := SliceByte { cell_cursor, m.cell_depth * m.slot_size } // slots = cell.slots slot_cursor := slots.data for;; { slot := slice(slot_cursor, m.slot_size) // slot = slots[slot_id] @@ -1065,9 +1074,9 @@ kt1cx_get :: proc(kt: KT1CX_Byte, key: u64, m: KT1CX_ByteMeta) -> ^byte { kt1cx_set :: proc(kt: KT1CX_Byte, key: u64, value: []byte, backing_cells: AllocatorInfo, m: KT1CX_ByteMeta) -> ^byte { hash_index := kt1cx_slot_id(kt, key, m) cell_offset := uintptr(hash_index) * uintptr(m.cell_size) - cell := SliceBytes{& kt.table[cell_offset], m.cell_size} + cell_cursor := cursor(kt.table)[cell_offset:] // KT1CX_Cell(Type) cell = kt.table[hash_index] { - slots := SliceBytes {cell.data, m.cell_depth * m.slot_size} + slots := SliceByte {cell_cursor, m.cell_depth * m.slot_size} // cell.slots slot_cursor := slots.data for ;; { @@ -1081,11 +1090,11 @@ kt1cx_set :: proc(kt: KT1CX_Byte, key: u64, value: []byte, backing_cells: Alloca return cast(^byte) slot_cursor } if slot_cursor == end(transmute([]byte) slots) { - curr_cell := transmute(^KT1CX_Byte_Cell) (uintptr(cell.data) + m.cell_next_offset) + curr_cell := transmute(^KT1CX_Byte_Cell) (uintptr(cell_cursor) + m.cell_next_offset) // curr_cell = cell if curr_cell != nil { slots.data = curr_cell.next slot_cursor = curr_cell.next - cell.data = curr_cell.next + cell_cursor = curr_cell.next continue } else { @@ -1271,10 +1280,18 @@ str8_fmt_kt1l :: proc(ainfo: AllocatorInfo, _buffer: ^[]byte, table: []KT1L_Slot } str8_fmt_backed :: proc(tbl_ainfo, buf_ainfo: AllocatorInfo, fmt_template: string, entries: [][2]string) -> string { - return {} + kt: []KT1L_Slot(string); kt1l_populate_slice_a2(string, & kt, tbl_ainfo, entries) + buf_size := Kilo * 64 + buffer := mem_alloc(buf_ainfo, buf_size) + result := str8_fmt_kt1l(buf_ainfo, & buffer, kt, fmt_template) + return result } str8_fmt_tmp :: proc(fmt_template: string, entries: [][2]string) -> string { - return {} + @static tbl_mem: [Kilo * 32]byte; tbl_arena := farena_make(tbl_mem[:]) + @static buf_mem: [Kilo * 64]byte; buffer := buf_mem[:] + kt: []KT1L_Slot(string); kt1l_populate_slice_a2(string, & kt, ainfo(& tbl_arena), entries) + result := str8_fmt_kt1l({}, & buffer, kt, fmt_template) + return result } Str8Cache_CELL_DEPTH :: 4 @@ -1288,39 +1305,129 @@ Str8Cache :: struct { tbl_backing: AllocatorInfo, kt: KT1CX_Str8, } -str8cache_init :: proc(cache: ^Str8Cache, str_reserve, cell_reserve, tbl_backing: AllocatorInfo, cell_pool_size, table_size: int) { +str8cache_init :: proc(cache: ^Str8Cache, str_reserve, cell_reserve, tbl_backing: AllocatorInfo, cell_pool_size := Kilo, table_size := Kilo) { + assert(cache != nil) + assert(str_reserve.procedure != nil) + assert(cell_reserve.procedure != nil) + assert(tbl_backing.procedure != nil) + cache.str_reserve = str_reserve + cache.cell_reserve = cell_reserve + cache.tbl_backing = tbl_backing + info := KT1CX_Info { + backing_cells = cell_reserve, + backing_table = tbl_backing, + } + m := KT1CX_InfoMeta { + cell_pool_size = cell_pool_size, + table_size = table_size, + slot_size = size_of(KT1CX_Slot_Str8), + slot_key_offset = offset_of(KT1CX_Slot_Str8, key), + cell_next_offset = offset_of(KT1CX_Cell_Str8, next), + cell_depth = Str8Cache_CELL_DEPTH, + type_width = size_of(string), + type = string + } + kt1cx_init(info, m, transmute(^KT1CX_Byte) & cache.kt) + return } str8cache_make :: proc(str_reserve, cell_reserve, tbl_backing: AllocatorInfo, cell_pool_size, table_size: int) -> Str8Cache { cache : Str8Cache; str8cache_init(& cache, str_reserve, cell_reserve, tbl_backing, cell_pool_size, table_size); return cache } str8cache_clear :: proc(kt: KT1CX_Str8) { + kt1cx_assert(kt) + kt1cx_clear(kt1cx_byte(kt), { + slot_size = size_of(KT1CX_Slot_Str8), + slot_key_offset = offset_of(KT1CX_Slot_Str8, key), + cell_next_offset = offset_of(KT1CX_Cell_Str8, next), + cell_depth = Str8Cache_CELL_DEPTH, + cell_size = size_of(KT1CX_Cell_Str8), + type_width = size_of(string), + type = string, + }) } str8cache_get :: proc(kt: KT1CX_Str8, key: u64) -> ^string { - return nil + kt1cx_assert(kt) + result := kt1cx_get(kt1cx_byte(kt), key, { + slot_size = size_of(KT1CX_Slot_Str8), + slot_key_offset = offset_of(KT1CX_Slot_Str8, key), + cell_next_offset = offset_of(KT1CX_Cell_Str8, next), + cell_depth = Str8Cache_CELL_DEPTH, + cell_size = size_of(KT1CX_Cell_Str8), + type_width = size_of(string), + type = string, + }) + return transmute(^string) result } str8cache_set :: proc(kt: KT1CX_Str8, key: u64, value: string, str_reserve, cell_reserve: AllocatorInfo) -> ^string { - return nil + kt1cx_assert(kt) + slice_assert(transmute([]byte) value) + assert(str_reserve.procedure != nil) + assert(cell_reserve.procedure != nil) + entry := kt1cx_set(kt1cx_byte(kt), key, transmute([]byte) value, cell_reserve, { + slot_size = size_of(KT1CX_Slot_Str8), + slot_key_offset = offset_of(KT1CX_Slot_Str8, key), + cell_next_offset = offset_of(KT1CX_Cell_Str8, next), + cell_depth = Str8Cache_CELL_DEPTH, + cell_size = size_of(KT1CX_Cell_Str8), + type_width = size_of(string), + type = string, + }) + assert(entry != nil) + result := transmute(^string) entry + is_empty := len(result) == 0 + if is_empty { + result ^ = transmute(string) alloc_slice(str_reserve, []u8, len(value)) + copy(result ^, value) + } + return result } -cache_str8 :: proc(cache: ^Str8Cache, str: string) -> ^string { - return nil +cache_str8 :: proc(cache: ^Str8Cache, str: string) -> string { + assert(cache != nil) + key: u64 = 0; hash64_djb8(& key, transmute([]byte) str) + result := str8cache_set(cache.kt, key, str, cache.str_reserve, cache.cell_reserve) + return result ^ } Str8Gen :: struct { backing: AllocatorInfo, - ptr: ^u8, - len: int, - cap: int, + ptr: [^]u8, + len: int, + cap: int, } str8gen_init :: proc(gen: ^Str8Gen, ainfo: AllocatorInfo) { - + assert(gen != nil) + gen.backing = ainfo + gen.ptr = raw_data(mem_alloc(ainfo, Kilo * 4)) + assert(gen.ptr != nil) + gen.len = 0 + gen.cap = Kilo * 4 } str8gen_make :: proc(ainfo: AllocatorInfo) -> Str8Gen { gen: Str8Gen; str8gen_init(& gen, ainfo); return gen } -str8gen_to_bytes :: proc(gen: Str8Gen) -> []byte { return transmute([]byte) SliceBytes {data = gen.ptr, len = gen.len} } -str8_from_str8gen :: proc(gen: Str8Gen) -> string { return transmute(string) SliceBytes {data = gen.ptr, len = gen.len} } +str8gen_to_bytes :: proc(gen: Str8Gen) -> []byte { return transmute([]byte) SliceByte {data = gen.ptr, len = gen.len} } +str8_from_str8gen :: proc(gen: Str8Gen) -> string { return transmute(string) SliceByte {data = gen.ptr, len = gen.len} } str8gen_append_str8 :: proc(gen: ^Str8Gen, str: string) { + result := mem_grow(gen.backing, str8gen_to_bytes(gen ^), len(str) + gen.len) + slice_assert(result) + to_copy := slice(cursor(result)[gen.len:], len(result) - gen.len) + copy(to_copy, transmute([]byte) str) + gen.ptr = transmute(^u8) raw_data(result) + gen.len = len(result) + gen.cap = max(gen.len, gen.cap) // TODO(Ed): Arenas currently hide total capacity before growth. Problably better todo classic append to actually track this. } str8gen_append_fmt :: proc(gen: ^Str8Gen, fmt_template: string, tokens: [][2]string) { + @static tbl_mem: [Kilo * 32]byte; tbl_arena := farena_make(tbl_mem[:]) + kt: []KT1L_Slot(string); kt1l_populate_slice_a2(string, & kt, ainfo(& tbl_arena), tokens) + buffer := slice(gen.ptr[gen.len:], gen.cap - gen.len) + if len(buffer) < Kilo * 16 { + result := mem_grow(gen.backing, str8gen_to_bytes(gen ^), Kilo * 16 + gen.cap) + slice_assert(result) + gen.ptr = raw_data(result) + gen.cap += Kilo * 16 + buffer = slice(gen.ptr[gen.len:], gen.cap - gen.len) + } + result := str8_fmt_kt1l(gen.backing, & buffer, kt, fmt_template) + gen.len += len(result) } //#endregion String Operations diff --git a/watl_exercise.proj b/watl_exercise.proj deleted file mode 100644 index 1295b03..0000000 --- a/watl_exercise.proj +++ /dev/null @@ -1,14 +0,0 @@ -// raddbg 0.9.18 project file - -recent_file: path: "c/watl.v0.msvc.c" -target: -{ - executable: "build/watl.v0.msvc.exe" - working_directory: C - enabled: 1 -} -breakpoint: -{ - source_location: "c/watl.v0.msvc.c:2227:1" - hit_count: 0 -} diff --git a/watl_exercise.user b/watl_exercise.user deleted file mode 100644 index 95d765b..0000000 --- a/watl_exercise.user +++ /dev/null @@ -1,154 +0,0 @@ -// raddbg 0.9.18 user file - -recent_project: path: "watl_exercise.proj" -window: -{ - size: 2048.000000 1152.000000 - pos: 234 234 - monitor: "\\\\.\\DISPLAY1" - maximized - panels: - { - 0.683614: selected getting_started text: - { - selected - expression: "file:\"C:/projects/WATL_Exercise/c/watl.v0.msvc.c\".data" - auto: 1 - query: input: "" - cursor_line: 2227 - cursor_column: 1 - mark_line: 2227 - mark_column: 1 - } - 0.316386: - { - watch: expression: "query:targets" - watch: - { - selected - expression: "" - watch: "listing.ptr.text()" - } - } - } -} -keybindings: -{ - { kill_all f5 shift } - { step_into_inst f11 alt } - { step_over_inst f10 alt } - { step_out f11 shift } - { halt x ctrl shift } - { halt pause } - { run f5 } - { restart f5 ctrl shift } - { step_into f11 } - { step_over f10 } - { run_to_line f10 ctrl } - { set_next_statement f10 ctrl shift } - { inc_window_font_size equal alt } - { dec_window_font_size minus alt } - { window n ctrl shift } - { toggle_fullscreen return ctrl } - { new_panel_right p ctrl } - { new_panel_down minus ctrl } - { rotate_panel_columns 2 ctrl } - { next_panel comma ctrl } - { prev_panel comma ctrl shift } - { focus_panel_right right ctrl alt } - { focus_panel_left left ctrl alt } - { focus_panel_up up ctrl alt } - { focus_panel_down down ctrl alt } - { undo z ctrl } - { redo y ctrl } - { go_back left alt } - { go_forward right alt } - { close_panel p ctrl shift alt } - { next_tab page_down ctrl } - { prev_tab page_up ctrl } - { next_tab tab ctrl } - { prev_tab tab ctrl shift } - { move_tab_right page_down ctrl shift } - { move_tab_left page_up ctrl shift } - { close_tab w ctrl } - { tab_bar_top up ctrl shift alt } - { tab_bar_bottom down ctrl shift alt } - { open_tab t ctrl } - { open o ctrl } - { switch i ctrl } - { switch_to_partner_file o alt } - { open_user n ctrl shift alt } - { open_project n ctrl alt } - { open_user o ctrl shift alt } - { open_project o ctrl alt } - { save_user s ctrl shift alt } - { save_project s ctrl shift } - { edit f2 } - { accept return } - { accept space } - { cancel esc } - { move_left left } - { move_right right } - { move_up up } - { move_down down } - { move_left_select left shift } - { move_right_select right shift } - { move_up_select up shift } - { move_down_select down shift } - { move_left_chunk left ctrl } - { move_right_chunk right ctrl } - { move_up_chunk up ctrl } - { move_down_chunk down ctrl } - { move_up_page page_up } - { move_down_page page_down } - { move_up_whole home ctrl } - { move_down_whole end ctrl } - { move_left_chunk_select left ctrl shift } - { move_right_chunk_select right ctrl shift } - { move_up_chunk_select up ctrl shift } - { move_down_chunk_select down ctrl shift } - { move_up_page_select page_up shift } - { move_down_page_select page_down shift } - { move_up_whole_select home ctrl shift } - { move_down_whole_select end ctrl shift } - { move_up_reorder up alt } - { move_down_reorder down alt } - { move_home home } - { move_end end } - { move_home_select home shift } - { move_end_select end shift } - { select_all a ctrl } - { delete_single delete } - { delete_chunk delete ctrl } - { backspace_single backspace } - { backspace_chunk backspace ctrl } - { copy c ctrl } - { copy insert ctrl } - { cut x ctrl } - { paste v ctrl } - { paste insert shift } - { insert_text null } - { move_next tab } - { move_prev tab shift } - { goto_line g ctrl } - { goto_address g alt } - { search f ctrl } - { search_backwards r ctrl } - { find_next f3 } - { find_prev f3 ctrl } - { find_selected_thread f4 } - { goto_name j ctrl } - { goto_name_at_cursor f12 } - { toggle_watch_expr_at_cursor w alt } - { toggle_watch_expr_at_mouse d ctrl } - { toggle_watch_pin f9 ctrl } - { toggle_breakpoint f9 } - { add_address_breakpoint f9 shift } - { add_function_breakpoint f9 ctrl shift } - { attach f6 shift } - { open_palette f1 } - { open_palette p ctrl shift } - { log_marker m ctrl shift alt } - { toggle_dev_menu d ctrl shift alt } -} -current_path: "C:/projects/WATL_Exercise/build"