1 /** 2 Copyright: Copyright (c) 2016, Joakim Brännström. All rights reserved. 3 License: MPL-2 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 6 This Source Code Form is subject to the terms of the Mozilla Public License, 7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain 8 one at http://mozilla.org/MPL/2.0/. 9 10 Utility useful for plugins. 11 */ 12 module dextool.plugin.utility; 13 14 import logger = std.experimental.logger; 15 16 import dextool.plugin.types : CliBasicOption, CliOptionParts; 17 18 version (unittest) { 19 import unit_threaded : shouldEqual; 20 } 21 22 /** Make a static c'tor that creates an instance with all class members initialized. 23 * 24 * Params: 25 * T = type to construct an instance of 26 * postInit = call the function with the initialized instance. 27 */ 28 mixin template MakerInitializingClassMembers(T, alias postInit = function void(ref T) { 29 }) { 30 static T make() { 31 T inst; 32 33 foreach (member; __traits(allMembers, T)) { 34 alias MemberT = typeof(__traits(getMember, inst, member)); 35 static if (is(MemberT == class)) { 36 __traits(getMember, inst, member) = new MemberT; 37 } 38 } 39 40 postInit(inst); 41 42 return inst; 43 } 44 } 45 46 /** Convenient array with support for marking of elements for later removal. 47 */ 48 struct MarkArray(T) { 49 import std.array : Appender; 50 51 alias Range = T[]; 52 private Appender!(size_t[]) remove_; 53 private Appender!(T*[]) arr; 54 55 /// Store e in the cache. 56 void put(T e) { 57 auto item = new T; 58 *item = e; 59 arr.put(item); 60 } 61 62 /// ditto 63 void put(T[] e) { 64 import std.algorithm : map; 65 66 foreach (b; e.map!((a) { auto item = new T; *item = a; return item; })) { 67 arr.put(b); 68 } 69 } 70 71 /// Retrieve a slice of the stored data. 72 auto data() { 73 import std.algorithm : map; 74 75 return arr.data.map!(a => *a); 76 } 77 78 /** Mark index `idx` for removal. 79 * 80 * Later as in calling $(D doRemoval). 81 */ 82 void markForRemoval(size_t idx) @safe pure { 83 remove_.put(idx); 84 } 85 86 /// Remove all items that has been marked. 87 void doRemoval() { 88 import std.algorithm : canFind, filter, map; 89 import std.range : enumerate; 90 91 // naive implementation. Should use swapping instead. 92 typeof(arr) new_; 93 new_.put(arr.data.enumerate.filter!(a => !canFind(remove_.data, 94 a.index)).map!(a => a.value)); 95 arr.clear; 96 remove_.clear; 97 98 arr = new_; 99 } 100 101 /// Clear the $(D MarkArray). 102 void clear() { 103 arr.clear; 104 remove_.clear; 105 } 106 } 107 108 @("Should store item") 109 unittest { 110 MarkArray!int arr; 111 112 arr.put(10); 113 114 arr.data.length.shouldEqual(1); 115 arr.data[0].shouldEqual(10); 116 } 117 118 @("Should mark and remove items") 119 unittest { 120 MarkArray!int arr; 121 arr.put([10, 20, 30]); 122 123 arr.markForRemoval(1); 124 arr.data.length.shouldEqual(3); 125 126 arr.doRemoval; 127 128 arr.data.length.shouldEqual(2); 129 arr.data[0].shouldEqual(10); 130 arr.data[1].shouldEqual(30); 131 }