scdoc: update news file
[supercollider.git] / SCClassLibrary / Common / Collections / String.sc
blobe5ff8bdd9f0a1f9a2c806817d6eb2441462ac842
1 String[char] : RawArray {
2         classvar <>unixCmdActions;
4         *initClass {
5                 unixCmdActions = IdentityDictionary.new;
6         }
8         *doUnixCmdAction {
9                 arg res, pid;
10                 unixCmdActions[pid].value(res, pid);
11                 unixCmdActions.removeAt(pid);
12         }
14         prUnixCmd {
15                 arg postOutput = true;
16                 _String_POpen
17                 ^this.primitiveFailed
18         }
20         // runs a unix command and sends stdout to the post window
21         unixCmd {
22                 arg action, postOutput = true;
23                 var pid;
24                 pid = this.prUnixCmd(postOutput);
25                 if(action.notNil) {
26                         unixCmdActions.put(pid, action);
27                 };
28                 ^pid;
29         }
31         // Like unixCmd but gets the result into a string
32         unixCmdGetStdOut {
33                 var pipe, lines, line;
35                 pipe = Pipe.new(this, "r");
36                 lines = "";
37                 line = pipe.getLine;
38                 while({line.notNil}, {lines = lines ++ line ++ "\n"; line = pipe.getLine; });
39                 pipe.close;
41                 ^lines;
42         }
44         asSymbol {
45                 _StringAsSymbol
46                 ^this.primitiveFailed
47         }
48         asInteger {
49                 _String_AsInteger
50                 ^this.primitiveFailed
51         }
52         asFloat {
53                 _String_AsFloat
54                 ^this.primitiveFailed
55         }
56         ascii {
57                 ^Array.fill(this.size, { |i| this[i].ascii })
58         }
60         stripRTF {
61                 _StripRtf
62                 ^this.primitiveFailed
63         }
65         stripHTML {
66                 _StripHtml
67                 ^this.primitiveFailed
68         }
70         *scDir {
71                 ^Platform.resourceDir
72         }
74         compare { arg aString, ignoreCase=false; _StringCompare }
75         < { arg aString; ^this.compare(aString, true) < 0 }
76         > { arg aString; ^this.compare(aString, true) > 0 }
77         <= { arg aString; ^this.compare(aString, true) <= 0 }
78         >= { arg aString; ^this.compare(aString, true) >= 0 }
79         == { arg aString; ^this.compare(aString, true) == 0 }
80         != { arg aString; ^this.compare(aString, true) != 0 }
81         hash { _StringHash }
83         // no sense doing collect as per superclass collection
84         performBinaryOpOnSimpleNumber { arg aSelector, aNumber;
85                 ^aNumber.asString.perform(aSelector, this);
86         }
87         performBinaryOpOnComplex { arg aSelector, aComplex;
88                 ^aComplex.asString.perform(aSelector, this);
89         }
91         isString { ^true }
92         asString { ^this }
93         asCompileString { _String_AsCompileString; }
94         species { ^String }
96         postln { _PostLine }
97         post { _PostString }
98         postcln { "// ".post; this.postln; }
99         postc { "// ".post; this.post; }
101         postf { arg ... items;  ^this.prFormat( items.collect(_.asString) ).post }
102         format { arg ... items; ^this.prFormat( items.collect(_.asString) ) }
103         prFormat { arg items; _String_Format ^this.primitiveFailed }
104         matchRegexp { arg string, start = 0, end; _String_Regexp ^this.primitiveFailed }
106         fformat { arg ... args;
107                 var str, resArgs, val, func;
108                 var suffixes, sig = false;
110                 this.do { |char|
111                         if(sig) {
112                                 val = args.removeAt(0);
113                                 func = Post.formats[char];
114                                 if(func.isNil) {
115                                         resArgs = resArgs.add(val);
116                                         str = str ++ char
117                                 } {
118                                         resArgs = resArgs.add(func.value(val))
119                                 };
120                                 sig = false;
121                         } {
122                                 str = str ++ char
123                         };
124                         if(char == $%) { sig = true };
125                 };
126                 ^str.format(*resArgs)
127         }
129         die { arg ... culprits;
130                 if(culprits.notEmpty,{
131                         ("\n\nFATAL ERROR: ").postln;
132                         culprits.do({ arg c; if(c.isString,{c.postln},{c.dump}) });
133                 });
134                 Error(this).throw;
135         }
136         error { "ERROR:\n".post; this.postln; }
137         warn { "WARNING:\n".post; this.postln }
138         inform { ^this.postln }
139         ++ { arg anObject; ^this prCat: anObject.asString; }
140         + { arg anObject; ^this prCat: " " prCat: anObject.asString; }
141         catArgs { arg ... items; ^this.catList(items) }
142         scatArgs { arg ... items; ^this.scatList(items) }
143         ccatArgs { arg ... items; ^this.ccatList(items) }
144         catList { arg list;
145                 // concatenate this with a list as a string
146                 var string = this.copy;
147                 list.do({ arg item, i;
148                         string = string ++ item;
149                 });
150                 ^string
151         }
152         scatList { arg list;
153                 var string = this.copy;
154                 list.do({ arg item, i;
155                         string = string prCat: " " ++ item;
156                 });
157                 ^string
158         }
159         ccatList { arg list;
160                 var string = this.copy;
161                 list.do({ arg item, i;
162                         string = string prCat: ", " ++ item;
163                 });
164                 ^string
165         }
166         split { arg separator=$/;
167                 var word="";
168                 var array=[];
169                 separator=separator.ascii;
171                 this.do({arg let,i;
172                         if(let.ascii != separator ,{
173                                 word=word++let;
174                         },{
175                                 array=array.add(word);
176                                 word="";
177                         });
178                 });
179                 ^array.add(word);
180         }
182         containsStringAt { arg index, string;
183                 ^compare( this[index..index + string.size-1], string, false) == 0
184         }
186         icontainsStringAt { arg index, string;
187                 ^compare( this[index..index + string.size-1], string, true) == 0
188         }
191         contains { arg string, offset = 0;
192                 ^this.find(string, false, offset).notNil
193         }
194         containsi { arg string, offset = 0;
195                 ^this.find(string, true, offset).notNil
196         }
198         findRegexp { arg regexp, offset = 0;
199        _String_FindRegexp
200        ^this.primitiveFailed
201         }
203         findAllRegexp { arg string, offset = 0;
204                 var indices = [], i=[];
205                 while {
206                         i = this.findRegexp(string, offset);
207                         i.notNil and: {i.size != 0}
208                 }{
209                         indices = indices.add(i);
210                         offset = i[0][0] + 1;
211                 }
212                 ^indices
213         }
215         find { arg string, ignoreCase = false, offset = 0;
216                 _String_Find
217                 ^this.primitiveFailed
218         }
219         findBackwards { arg string, ignoreCase = false, offset = 0x7FFFFFFE;
220                 _String_FindBackwards
221                 ^this.primitiveFailed
222         }
223         endsWith { arg string;
224                 ^this.contains(string, this.size - string.size)
225         }
226         beginsWith { arg string;
227                 ^this.containsStringAt(0, string)
228         }
229         findAll { arg string, ignoreCase = false, offset=0;
230                 var indices, i=0;
231                 while {
232                         i = this.find(string, ignoreCase, offset);
233                         i.notNil
234                 }{
235                         indices = indices.add(i);
236                         offset = i + 1;
237                 }
238                 ^indices
239         }
240         replace { arg find, replace;
241                 ^super.replace(find, replace).join
242         }
245         escapeChar { arg charToEscape; // $"
246                 _String_EscapeChar
247         }
248         shellQuote {
249                 ^"'"++this.replace("'","'\\''")++"'"
250         }
251         quote {
252                 ^"\"" ++ this ++ "\""
253         }
254         tr { arg from,to;
255                 ^this.collect({ arg char;
256                         if(char == from,{to},{char})
257                 })
258         }
260         insert { arg index, string;
261                 ^this.keep(index) ++ string ++ this.drop(index)
262         }
264         wrapExtend { arg size;
265                 ^this.dup(size div: this.size).join ++ this.keep(size % this.size)
266         }
268         zeroPad {
269                 ^" " ++ this ++ " "
270         }
272         padLeft { arg size, string = " ";
273                 ^string.wrapExtend(max(0, size - this.size)) ++ this
274         }
276         padRight { arg size, string = " ";
277                 ^this ++ string.wrapExtend(max(0, size - this.size))
278         }
280         underlined { arg char = $-;
281                 ^this ++ "\n" ++ String.fill(this.size, char)
282         }
284         scramble {
285                 ^this.as(Array).scramble.as(String)
286         }
288         rotate { arg n = 1;
289                 ^this.as(Array).rotate(n).as(String)
290         }
292         compile { ^thisProcess.interpreter.compile(this); }
293         interpret { ^thisProcess.interpreter.interpret(this); }
294         interpretPrint { ^thisProcess.interpreter.interpretPrint(this); }
296         *readNew { arg file;
297                 ^file.readAllString;
298         }
299         prCat { arg aString; _ArrayCat }
301         printOn { arg stream;
302                 stream.putAll(this);
303         }
304         storeOn { arg stream;
305                 stream.putAll(this.asCompileString);
306         }
308         inspectorClass { ^StringInspector }
310         /// unix
312         standardizePath {
313                 _String_StandardizePath
314                 ^this.primitiveFailed
315         }
316         realPath {
317                 _String_RealPath
318                 ^this.primitiveFailed
319         }
320         withTrailingSlash {
321                 var sep = thisProcess.platform.pathSeparator;
322                 if(this.last != sep, {
323                         ^this ++ sep
324                 },{
325                         ^this
326                 })
327         }
328         withoutTrailingSlash {
329                 var sep = thisProcess.platform.pathSeparator;
330                 if(this.last == sep,{
331                         ^this.copyRange(0, this.size-2)
332                 },{
333                         ^this
334                 })
335         }
337         absolutePath {
338                 var first, sep;
339                 sep = thisProcess.platform.pathSeparator;
340                 first = this[0];
341                 if(first == sep){^this};
342                 if(first == $~){^this.standardizePath};
343                 ^File.getcwd ++ sep ++ this;
344         }
346         pathMatch { _StringPathMatch ^this.primitiveFailed } // glob
347         load {
348                 ^thisProcess.interpreter.executeFile(this);
349         }
350         loadPaths { |warn=true|
351                 var paths = this.pathMatch;
352                 if(warn and:{paths.isEmpty}) { ("no files found for this path:" + this.quote).warn };
353                 ^paths.collect({ arg path;
354                         thisProcess.interpreter.executeFile(path);
355                 });
356         }
357         loadRelative {
358                 var path = thisProcess.nowExecutingPath;
359                 if(path.isNil) { Error("can't load relative to an unsaved file").throw};
360                 if(path.basename == this) { Error("should not load a file from itself").throw };
361                 ^(path.dirname ++ thisProcess.platform.pathSeparator ++ this).loadPaths
362         }
363         resolveRelative {
364                 var path, caller;
365                 caller = thisMethod.getBackTrace.caller.functionDef;
366                 if(caller.isKindOf(Method) && (caller != Interpreter.findMethod(\interpretPrintCmdLine)), {
367                         path = caller.filenameSymbol.asString;
368                 }, {
369                         path = thisProcess.nowExecutingPath;
370                 });
371                 if(this[0] == thisProcess.platform.pathSeparator, {^this});
372                 if(path.isNil) { Error("can't resolve relative to an unsaved file").throw};
373                 ^(path.dirname ++ thisProcess.platform.pathSeparator ++ this)
374         }
375         include {
376                 if(Quarks.isInstalled(this).not) {
377                         Quarks.install(this);
378                         "... the class library may have to be recompiled.".postln;
379                         // maybe check later whether there are .sc files included.
380                 }
381         }
382         exclude {
383                 if(Quarks.isInstalled(this)) {
384                         Quarks.uninstall(this);
385                         "... the class library may have to be recompiled.".postln;
386                 }
387         }
388         basename {
389                 _String_Basename;
390                 ^this.primitiveFailed
391         }
392         dirname {
393                 _String_Dirname;
394                 ^this.primitiveFailed
395         }
396         splitext {
397                 this.reverseDo({ arg char, i;
398                         if (char == $\., {
399                                 ^[this.copyFromStart(this.size - 2 - i), this.copyToEnd(this.size - i)]
400                         });
401                 });
402                 ^[this, nil]
403         }
405         // path concatenate
406         +/+ { arg path;
407                 var pathSeparator = thisProcess.platform.pathSeparator;
409                 if (path.respondsTo(\fullPath)) {
410                         ^PathName(this +/+ path.fullPath)
411                 };
413                 if (this.last == pathSeparator or: { path.first == pathSeparator }) {
414                         ^this ++ path
415                 };
417                 ^this ++ pathSeparator ++ path
418         }
420         asRelativePath { |relativeTo|
421                 ^PathName(this).asRelativePath(relativeTo)
422         }
423         asAbsolutePath {
424                         // changed because there is no need to create a separate object
425                         // when String already knows how to make an absolute path
426                 ^this.absolutePath;  // was ^PathName(this).asAbsolutePath
427         }
429         // runs a unix command and returns the result code.
430         systemCmd { _String_System ^this.primitiveFailed }
432         gethostbyname { _GetHostByName ^this.primitiveFailed }
434         // gets value of environment variable
435         getenv {
436                 _String_Getenv
437                 ^this.primitiveFailed
438         }
439         // sets value of environment variable
440         // value may be nil to unset the variable
441         setenv { arg value;
442                 _String_Setenv
443                 ^this.primitiveFailed
444         }
445         unsetenv {
446                 ^this.setenv(nil)
447         }
449         /// code gen
450         codegen_UGenCtorArg { arg stream;
451                 stream << this.asCompileString;
452         }
453         ugenCodeString { arg ugenIndex, isDecl, inputNames=#[], inputStrings=#[];
454                 _UGenCodeString
455                 ^this.primitiveFailed
456         }
458         asSecs { |maxDays = 365| // assume a timeString of ddd:hh:mm:ss.sss. see asTimeString.
459                 var time = 0, sign = 1, str = this;
460                 var limits = [inf, 60, 60, 24, maxDays];
461                 var scaling = [0.001, 1.0, 60.0, 3600.0, 86400.0];
462                 var padding = [3, 2, 2, 2, 3];
464                 if (this.first == $-) {
465                         str = this.drop(1);
466                         sign = -1
467                 };
469                 str.split($:).reverseDo { |num, i|
470                         num = num.padRight(padding[i], "0").asInteger;
471                         if (num > limits[i]) {
472                                 ("asSecs: number greater than allowed:" + this).warn;
473                                 num = limits[i];
474                         };
475                         if (num < 0) {
476                                 ("asSecs: negative numbers within slots not supported:" + this).warn;
477                                 num = 0;
478                         };
479                         time = time + (num * scaling[i]);
480                 };
481                 ^time * sign;
482         }
484         speak { arg channel = 0, force = false;
485                 var speech = GUI.current.speech;
486                 if( speech.initialized.not, { speech.init });
487                 speech.channels[ channel ].speak( this, force );
488         }
490         toLower {
491                 ^this.collect(_.toLower)
492         }
493         toUpper {
494                 ^this.collect(_.toUpper)
495         }
497         mkdir {
498                 File.mkdir(this);
499         }