├── images
└── logo.png
├── Symbol Instance Renamer.sketchplugin
└── Contents
│ ├── Resources
│ └── icon.png
│ └── Sketch
│ ├── manifest.json
│ └── script.js
├── appcast.xml
├── LICENSE.md
└── README.md
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonburn/symbol-instance-renamer/HEAD/images/logo.png
--------------------------------------------------------------------------------
/Symbol Instance Renamer.sketchplugin/Contents/Resources/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sonburn/symbol-instance-renamer/HEAD/Symbol Instance Renamer.sketchplugin/Contents/Resources/icon.png
--------------------------------------------------------------------------------
/appcast.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Symbol Instance Renamer
5 | http://sparkle-project.org/files/sparkletestcast.xml
6 | Rename all symbol instances to the name of their master.
7 | en
8 | -
9 | Version 2.9
10 |
11 |
13 | Fix for Sketch 72.
14 |
15 | ]]>
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Jason Burns (Sonburn)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | Rename symbol instances to the name of their master.
4 |
5 | [](https://www.youtube.com/watch?v=_L7E0B3y9d0)
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | # Usage
16 |
17 | * cmd option shift d - Rename all symbol instances on all pages to the name of their master
18 | * cmd option shift x - Rename all symbol instances on all pages, except the Symbols page, to the name of their master
19 | * cmd option shift p - Rename all symbol instances on current page to the name of their master
20 | * cmd option shift a - Rename all symbol instances in selected artboards to the name of their master
21 | * cmd option shift m - Rename all instances of the selected symbols to the name of their master
22 | * cmd option shift s - Rename selected symbol instances to the name of their master
23 |
24 | ## Automatic
25 | Search for Symbol Instance Renamer in [Sketchrunner](http://sketchrunner.com/) or [Sketch Toolbox](http://sketchtoolbox.com/) if you have one of those installed.
26 |
27 | Once installed, Sketch will automatically notify you when an update is available (version 2.2 and later).
28 |
29 | ## Manual
30 |
31 | 1. Download and open symbol-instance-renamer-master.zip
32 | 2. Navigate to Symbol Instance Renamer.sketchplugin and copy/move to your plugins directory
33 |
34 | To find your plugins directory...
35 |
36 | 1. In the Sketch menu, navigate to Plugins > Manage Plugins...
37 | 2. Click the cog in the lower left of the plugins window, and click Reveal Plugins Folder
38 |
39 | # Changelog
40 |
41 | * **2.9** - Fix for Sketch 72.
42 | * **2.8** - Fixed 'Rename Selected Instance(s) with Options', and added handling for when a symbol instance's master is missing.
43 | * **2.7** - Update for Sketch 53.
44 | * **2.6** - Added new function to rename selected instances with additional options, and added plugin icon to manifest for Sketch 50.
45 | * **2.5** - Added new function to rename all instances within selected artboard(s).
46 | * **2.4** - Added new function to rename all instances of selected symbol(s).
47 | * **2.3** - Added new function to rename all instances on all pages, except Symbols page. Added fix for when the master for an instance is missing.
48 | * **2.2** - Added appcast plugin support for Sketch 45 and later. Added new function to rename all instances on current page. New shortcuts.
49 |
50 | # Contact
51 |
52 | Find me on Twitter
53 |
54 | # Support
55 |
56 | If you find this plugin helpful, or would like to support my plugins in general, buy me ☕️ via PayPal.
57 |
58 | # License
59 |
60 | Copyright (c) 2021 Jason Burns (Sonburn). See LICENSE.md for further details.
61 |
--------------------------------------------------------------------------------
/Symbol Instance Renamer.sketchplugin/Contents/Sketch/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name" : "Symbol Instance Renamer",
3 | "description" : "Rename symbol instances to the name of their master.",
4 | "author" : "Jason Burns",
5 | "homepage" : "https://github.com/sonburn/symbol-instance-renamer",
6 | "version" : "2.9",
7 | "identifier" : "com.sonburn.sketchplugins.symbol-instance-renamer",
8 | "appcast" : "https://raw.githubusercontent.com/sonburn/symbol-instance-renamer/master/appcast.xml",
9 | "icon" : "icon.png",
10 | "commands" : [
11 | {
12 | "name" : "Rename All Instances on All Pages",
13 | "shortcut" : "cmd option shift d",
14 | "identifier" : "renameEverything",
15 | "description" : "Rename all symbol instances on all pages to the name of their master.",
16 | "icon" : "icon.png",
17 | "script" : "script.js",
18 | "handler" : "renameEverything"
19 | },
20 | {
21 | "name" : "Rename All Instances on All Pages (Except Symbols Page)",
22 | "shortcut" : "cmd option shift x",
23 | "identifier" : "renamePages",
24 | "description" : "Rename all symbol instances on all pages, except the Symbols page, to the name of their master.",
25 | "icon" : "icon.png",
26 | "script" : "script.js",
27 | "handler" : "renamePages"
28 | },
29 | {
30 | "name" : "Rename All Instances on Current Page",
31 | "shortcut" : "cmd option shift p",
32 | "identifier" : "renamePage",
33 | "description" : "Rename all symbol instances on current page to the name of their master.",
34 | "icon" : "icon.png",
35 | "script" : "script.js",
36 | "handler" : "renamePage"
37 | },
38 | {
39 | "name" : "Rename All Instances in Selected Artboard(s)",
40 | "shortcut" : "cmd option shift a",
41 | "identifier" : "renameArtboard",
42 | "description" : "Rename all symbol instances in selected artboards to the name of their master.",
43 | "icon" : "icon.png",
44 | "script" : "script.js",
45 | "handler" : "renameArtboard"
46 | },
47 | {
48 | "name" : "Rename All Instances of Selected Symbol(s)",
49 | "shortcut" : "cmd option shift m",
50 | "identifier" : "renameSymbol",
51 | "description" : "Rename all instances of the selected symbols to the name of their master.",
52 | "icon" : "icon.png",
53 | "script" : "script.js",
54 | "handler" : "renameSymbol"
55 | },
56 | {
57 | "name" : "Rename Selected Instance(s)",
58 | "shortcut" : "cmd option shift s",
59 | "identifier" : "renameSelected",
60 | "description" : "Rename selected symbol instances to the name of their master.",
61 | "icon" : "icon.png",
62 | "script" : "script.js",
63 | "handler" : "renameSelected"
64 | },
65 | {
66 | "name" : "Rename Selected Instance(s) with Options…",
67 | "identifier" : "renameSelectedOptions",
68 | "description" : "Rename instances with additional options.",
69 | "icon" : "icon.png",
70 | "script" : "script.js",
71 | "handler" : "renameSelectedOptions"
72 | },
73 | {
74 | "name" : "Report Issue",
75 | "identifier" : "report",
76 | "description" : "Report an issue with Symbol Instance Renamer.",
77 | "icon" : "icon.png",
78 | "script" : "script.js",
79 | "handler" : "report"
80 | },
81 | {
82 | "name" : "Other Plugins",
83 | "identifier" : "plugins",
84 | "description" : "View all of Sonburn's plugins.",
85 | "icon" : "icon.png",
86 | "script" : "script.js",
87 | "handler" : "plugins"
88 | },
89 | {
90 | "name" : "Donate",
91 | "identifier" : "donate",
92 | "description" : "Donate to the development of Symbol Instance Renamer.",
93 | "icon" : "icon.png",
94 | "script" : "script.js",
95 | "handler" : "donate"
96 | }
97 | ],
98 | "menu" : {
99 | "title" : "Symbol Instance Renamer",
100 | "items" : [
101 | "renameEverything",
102 | "renamePages",
103 | "renamePage",
104 | "renameArtboard",
105 | "renameSymbol",
106 | "renameSelected",
107 | "renameSelectedOptions",
108 | "-",
109 | "report",
110 | "plugins",
111 | "donate"
112 | ]
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/Symbol Instance Renamer.sketchplugin/Contents/Sketch/script.js:
--------------------------------------------------------------------------------
1 | const sketch = require("sketch");
2 |
3 | var strPluginName = "Symbol Instance Renamer",
4 | strRenameSuccess = " symbol instances have been renamed",
5 | strRenameFailure = "Select at least one symbol instance to rename.",
6 | strRenameFailureArtboard = "Select at least one artboard for which to rename instances.",
7 | strRenameFailureSymbol = "Select at least one symbol master for which to rename instances.",
8 | strRenameFailureString = "Provide a new name for the selected instances.",
9 | debugMode = false;
10 |
11 | var renameEverything = function(context) {
12 | var pages = context.document.pages(),
13 | pageLoop = pages.objectEnumerator(),
14 | page,
15 | count = 0;
16 |
17 | while (page = pageLoop.nextObject()) {
18 | count = count + renameObjectInstances(page);
19 | }
20 |
21 | sketch.UI.message(count + strRenameSuccess);
22 |
23 | if (!debugMode) googleAnalytics(context,"rename","everything");
24 | }
25 |
26 | var renamePages = function(context) {
27 | var pages = context.document.pages(),
28 | pageLoop = pages.objectEnumerator(),
29 | page,
30 | count = 0;
31 |
32 | while (page = pageLoop.nextObject()) {
33 | if (page != context.document.documentData().symbolsPage()) {
34 | count = count + renameObjectInstances(page);
35 | }
36 | }
37 |
38 | sketch.UI.message(count + strRenameSuccess);
39 |
40 | if (!debugMode) googleAnalytics(context,"rename","pages");
41 | }
42 |
43 | var renamePage = function(context) {
44 | var count = renameObjectInstances(context.document.currentPage());
45 |
46 | sketch.UI.message(count + strRenameSuccess);
47 |
48 | if (!debugMode) googleAnalytics(context,"rename","page");
49 | }
50 |
51 | var renameArtboard = function(context) {
52 | var predicate = NSPredicate.predicateWithFormat("className == %@","MSArtboardGroup"),
53 | artboards = context.selection.filteredArrayUsingPredicate(predicate),
54 | artboardLoop = artboards.objectEnumerator(),
55 | artboard,
56 | count = 0;
57 |
58 | if (artboards.count() > 0) {
59 | while (artboard = artboardLoop.nextObject()) {
60 | count = count + renameObjectInstances(artboard);
61 | }
62 |
63 | sketch.UI.message(count + strRenameSuccess);
64 |
65 | if (!debugMode) googleAnalytics(context,"rename","artboard");
66 | } else {
67 | sketch.UI.alert(strPluginName,strRenameFailureArtboard);
68 | }
69 | }
70 |
71 | var renameSymbol = function(context) {
72 | var predicate = NSPredicate.predicateWithFormat("className == %@","MSSymbolMaster"),
73 | symbols = context.selection.filteredArrayUsingPredicate(predicate),
74 | symbolLoop = symbols.objectEnumerator(),
75 | symbol,
76 | count = 0;
77 |
78 | if (symbols.count() > 0) {
79 | while (symbol = symbolLoop.nextObject()) {
80 | var instances = symbol.allInstances(),
81 | instanceLoop = instances.objectEnumerator(),
82 | instance;
83 |
84 | while (instance = instanceLoop.nextObject()) {
85 | if (renameInstance(instance)) count++;
86 | }
87 | }
88 |
89 | sketch.UI.message(count + strRenameSuccess);
90 |
91 | if (!debugMode) googleAnalytics(context,"rename","symbol");
92 | } else {
93 | sketch.UI.alert(strPluginName,strRenameFailureSymbol);
94 | }
95 | }
96 |
97 | var renameSelected = function(context) {
98 | var predicate = NSPredicate.predicateWithFormat("className == %@","MSSymbolInstance"),
99 | instances = context.selection.filteredArrayUsingPredicate(predicate),
100 | instanceLoop = instances.objectEnumerator(),
101 | instance,
102 | count = 0;
103 |
104 | if (instances.count() > 0) {
105 | while (instance = instanceLoop.nextObject()) {
106 | if (renameInstance(instance)) count++;
107 | }
108 |
109 | sketch.UI.message(count + strRenameSuccess);
110 |
111 | if (!debugMode) googleAnalytics(context,"rename","instance");
112 | } else {
113 | sketch.UI.alert(strPluginName,strRenameFailure);
114 | }
115 | }
116 |
117 | var renameSelectedOptions = function(context) {
118 | var predicate = NSPredicate.predicateWithFormat("className == %@","MSSymbolInstance"),
119 | selections = context.selection.filteredArrayUsingPredicate(predicate),
120 | selectionLoop = selections.objectEnumerator(),
121 | selection,
122 | count = 0;
123 |
124 | if (selections.count() > 0) {
125 | var alertWindow = COSAlertWindow.new(),
126 | iconPath = context.plugin.urlForResourceNamed("icon.png").path(),
127 | icon = NSImage.alloc().initByReferencingFile(iconPath),
128 | renameSource = createRadios(["Use symbol master name","Use other name…"],0),
129 | renameTo = createField("",""),
130 | renameSiblings = createCheckbox({name:"Rename all instances of selected instances",value:1},0),
131 | renameMaster = createCheckbox({name:"Rename symbol master (local name only)",value:1},1),
132 | buttonRename = alertWindow.addButtonWithTitle("Rename"),
133 | buttonCancel = alertWindow.addButtonWithTitle("Cancel");
134 |
135 | renameSource.cells().objectAtIndex(0).setAction("callAction:");
136 | renameSource.cells().objectAtIndex(0).setCOSJSTargetFunction(function(sender) {
137 | renameTo.setEnabled(0);
138 | renameMaster.setEnabled(0);
139 | });
140 |
141 | renameSource.cells().objectAtIndex(1).setAction("callAction:");
142 | renameSource.cells().objectAtIndex(1).setCOSJSTargetFunction(function(sender) {
143 | renameTo.setEnabled(1);
144 | renameTo.becomeFirstResponder();
145 | renameMaster.setEnabled(1);
146 | });
147 |
148 | alertWindow.setIcon(icon);
149 | alertWindow.setMessageText(strPluginName);
150 | alertWindow.setInformativeText("Rename selected symbol instances...");
151 | alertWindow.addAccessoryView(renameSource);
152 | alertWindow.addAccessoryView(renameTo);
153 | alertWindow.addAccessoryView(renameMaster);
154 | alertWindow.addAccessoryView(renameSiblings);
155 |
156 | renameTo.setEnabled(0);
157 | renameMaster.setEnabled(0);
158 |
159 | setKeyOrder(alertWindow,[
160 | renameSource,
161 | renameTo,
162 | renameMaster,
163 | renameSiblings,
164 | buttonRename
165 | ]);
166 |
167 | var alertResponse = alertWindow.runModal();
168 |
169 | if (alertResponse == 1000) {
170 | if (renameSource.selectedCell().tag() == 1 && renameTo.stringValue() == "") {
171 | sketch.UI.alert(strPluginName,strRenameFailureString);
172 | } else {
173 | var name = (renameTo.stringValue() != "") ? renameTo.stringValue() : false,
174 | count = 0;
175 |
176 | if (renameSiblings.state() == 1) {
177 | var masters = NSMutableArray.array();
178 |
179 | while (selection = selectionLoop.nextObject()) {
180 | var master = selection.symbolMaster();
181 |
182 | if (!masters.containsObject(master)) {
183 | masters.addObject(master);
184 | }
185 | }
186 |
187 | var masterLoop = masters.objectEnumerator(),
188 | master;
189 |
190 | while (master = masterLoop.nextObject()) {
191 | var instances = master.allInstances(),
192 | instanceLoop = instances.objectEnumerator(),
193 | instance;
194 |
195 | while (instance = instanceLoop.nextObject()) {
196 | if (renameInstance(instance,name)) count++;
197 | }
198 |
199 | if (renameSource.selectedCell().tag() == 1 && renameMaster.state() == 1) {
200 | master.setName(name);
201 | }
202 | }
203 | } else {
204 | while (selection = selectionLoop.nextObject()) {
205 | if (renameInstance(selection,name)) count++;
206 |
207 | if (renameSource.selectedCell().tag() == 1 && renameMaster.state() == 1) {
208 | selection.symbolMaster().setName(name);
209 | }
210 | }
211 | }
212 |
213 | sketch.UI.message(count + strRenameSuccess);
214 |
215 | if (!debugMode) googleAnalytics(context,"rename","instanceOptions");
216 | }
217 | } else return false;
218 | } else {
219 | sketch.UI.alert(strPluginName,strRenameFailure);
220 | }
221 | }
222 |
223 | var report = function(context) {
224 | openUrl("https://github.com/sonburn/symbol-instance-renamer/issues/new");
225 |
226 | if (!debugMode) googleAnalytics(context,"report","report");
227 | }
228 |
229 | var plugins = function(context) {
230 | openUrl("https://sonburn.github.io/");
231 |
232 | if (!debugMode) googleAnalytics(context,"plugins","plugins");
233 | }
234 |
235 | var donate = function(context) {
236 | openUrl("https://www.paypal.me/sonburn");
237 |
238 | if (!debugMode) googleAnalytics(context,"donate","donate");
239 | }
240 |
241 | function createCheckbox(item,flag,frame) {
242 | var frame = (frame) ? frame : NSMakeRect(0,0,300,16),
243 | checkbox = NSButton.alloc().initWithFrame(frame),
244 | flag = (flag == false) ? NSOffState : NSOnState;
245 |
246 | checkbox.setButtonType(NSSwitchButton);
247 | checkbox.setBezelStyle(0);
248 | checkbox.setTitle(item.name);
249 | checkbox.setTag(item.value);
250 | checkbox.setState(flag);
251 |
252 | return checkbox;
253 | }
254 |
255 | function createField(string,placeholder,frame) {
256 | var frame = (frame) ? frame : NSMakeRect(0,0,300,24),
257 | field = NSTextField.alloc().initWithFrame(frame);
258 |
259 | field.setStringValue(string);
260 | field.setPlaceholderString(placeholder);
261 |
262 | return field;
263 | }
264 |
265 | function createRadios(options,selected,format,x,y) {
266 | var rows = options.length,
267 | columns = 1,
268 | matrixWidth = 300,
269 | cellWidth = matrixWidth,
270 | x = (x) ? x : 0,
271 | y = (y) ? y : 0;
272 |
273 | if (format && format != 0) {
274 | rows = options.length / 2;
275 | columns = 2;
276 | matrixWidth = 300;
277 | cellWidth = matrixWidth / columns;
278 | }
279 |
280 | var cell = NSButtonCell.alloc().init();
281 |
282 | cell.setButtonType(NSRadioButton);
283 |
284 | var matrix = NSMatrix.alloc().initWithFrame_mode_prototype_numberOfRows_numberOfColumns(
285 | NSMakeRect(x,y,matrixWidth,rows*20),
286 | NSRadioModeMatrix,
287 | cell,
288 | rows,
289 | columns
290 | );
291 |
292 | matrix.setCellSize(NSMakeSize(cellWidth,20));
293 |
294 | for (i = 0; i < options.length; i++) {
295 | matrix.cells().objectAtIndex(i).setTitle(options[i]);
296 | matrix.cells().objectAtIndex(i).setTag(i);
297 | }
298 |
299 | matrix.selectCellAtRow_column(selected,0);
300 |
301 | return matrix;
302 | }
303 |
304 | function createSelect(items,selected,frame) {
305 | var comboBox = NSComboBox.alloc().initWithFrame(frame),
306 | selected = (selected > -1) ? selected : 0;
307 |
308 | comboBox.addItemsWithObjectValues(items);
309 | comboBox.selectItemAtIndex(selected);
310 | comboBox.setNumberOfVisibleItems(16);
311 | comboBox.setCompletes(1);
312 |
313 | return comboBox;
314 | }
315 |
316 | function googleAnalytics(context,category,action,label,value) {
317 | var trackingID = "UA-118918252-1",
318 | uuidKey = "google.analytics.uuid",
319 | uuid = NSUserDefaults.standardUserDefaults().objectForKey(uuidKey);
320 |
321 | if (!uuid) {
322 | uuid = NSUUID.UUID().UUIDString();
323 | NSUserDefaults.standardUserDefaults().setObject_forKey(uuid,uuidKey);
324 | }
325 |
326 | var url = "https://www.google-analytics.com/collect?v=1";
327 | // Tracking ID
328 | url += "&tid=" + trackingID;
329 | // Source
330 | url += "&ds=sketch" + sketch.version.sketch;
331 | // Client ID
332 | url += "&cid=" + uuid;
333 | // pageview, screenview, event, transaction, item, social, exception, timing
334 | url += "&t=event";
335 | // App Name
336 | url += "&an=" + encodeURI(context.plugin.name());
337 | // App ID
338 | url += "&aid=" + context.plugin.identifier();
339 | // App Version
340 | url += "&av=" + context.plugin.version();
341 | // Event category
342 | url += "&ec=" + encodeURI(category);
343 | // Event action
344 | url += "&ea=" + encodeURI(action);
345 | // Event label
346 | if (label) {
347 | url += "&el=" + encodeURI(label);
348 | }
349 | // Event value
350 | if (value) {
351 | url += "&ev=" + encodeURI(value);
352 | }
353 |
354 | var session = NSURLSession.sharedSession(),
355 | task = session.dataTaskWithURL(NSURL.URLWithString(NSString.stringWithString(url)));
356 |
357 | task.resume();
358 | }
359 |
360 | function openUrl(url) {
361 | NSWorkspace.sharedWorkspace().openURL(NSURL.URLWithString(url));
362 | }
363 |
364 | function renameInstance(instance,name) {
365 | if (instance.symbolMaster()) {
366 | if (name) {
367 | instance.setName(name);
368 | return true;
369 | } else if (instance.name() != instance.symbolMaster().name().trim()) {
370 | instance.setName(instance.symbolMaster().name());
371 | return true;
372 | } else return false;
373 | } else {
374 | log(instance.name() + ' might have a missing symbol master');
375 | }
376 | }
377 |
378 | function renameObjectInstances(object) {
379 | var predicate = NSPredicate.predicateWithFormat("className == %@","MSSymbolInstance"),
380 | instances = object.children().filteredArrayUsingPredicate(predicate),
381 | instanceLoop = instances.objectEnumerator(),
382 | instance,
383 | count = 0;
384 |
385 | while (instance = instanceLoop.nextObject()) {
386 | if (renameInstance(instance)) count++;
387 | }
388 |
389 | return count;
390 | }
391 |
392 | function setKeyOrder(alert,order) {
393 | for (var i = 0; i < order.length; i++) {
394 | var thisItem = order[i],
395 | nextItem = order[i+1];
396 |
397 | if (nextItem) thisItem.setNextKeyView(nextItem);
398 | }
399 |
400 | alert.alert().window().setInitialFirstResponder(order[0]);
401 | }
402 |
--------------------------------------------------------------------------------