File: sl_utils.sl

package info (click to toggle)
jed-extra 2.5.7-2
  • links: PTS, VCS
  • area: main
  • in suites: buster, stretch
  • size: 2,972 kB
  • ctags: 2,490
  • sloc: makefile: 75; ruby: 43; sed: 38; sh: 31
file content (367 lines) | stat: -rw-r--r-- 11,919 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
% Various programming utils that are used by most of my other modes.
%
% Copyright (c) 2006 Guenter Milde (milde users.sf.net)
% Released under the terms of the GNU General Public License (ver. 2 or later)
%
% Version    1.0   first public release
%            1.1   new: max(), contract_filename()
%            1.2   new: normalize_modename(), what_line_if_wide()
%            1.3   backwards compatibility: emulate run_program() if not
%                  existent (works only in xjed)
% 2004-03-22 1.3.1 bugfix in contract_filename() error if  HOME
%                  environment variable is missing (report Thomas Koeckritz)
%            1.3.2 removed max(), as it contradicts the intrinsic max()
%                  definition (which resembles array_max() from datutils.sl)
% 2005-04-11 1.4   new function prompt_for_argument()
%                  added provide("sl_utils")
%            1.5   new function _implements(): implement a "named" namespace
%                  but allow re-evaluation if `_debug_info` is TRUE
% 2005-05-23 1.5.1 bugfix in _implements(): separate _implement and provide,
%                  do not rely on _featurep()
% 2005-06-07 1.5.2 moved run_program emulation to compat16-15.sl
% 2005-10-05 1.5.3 Simplified _implements(). Developers working with SLang2 
%                  should switch to using the standard implements().
%                  (Normal users will will usually not re-evaluate.)
%            1.5.4 Documentation update for run_function mentioning call_function
%            1.5.5 Documentation fix for push_defaults()
% 2008-01-11 1.5.6 Documentation update for run_function() and get_blocal()

% _debug_info = 1;

provide("sl_utils");

%!%+
%\function{push_defaults}
%\synopsis{Push N args to the stack}
%\usage{(a_1, ..., a_N) = push_defaults(d_1, ..., d_N, N)}
%\description
% Push N args to the stack. Together with \var{_NARGS} this enables the
% definition of slang functions with optional arguments.
%\example
% A function with one compulsory and two optional arguments
%#v+
%   define fun() % (a1, a2="d2", a3=whatbuf())
%   {
%      variable a1, a2, a3;
%      (a1, a2, a3) = push_defaults( , "d2", whatbuf(), _NARGS-1);
%      vmessage("(%S, %S, %S)", a1, a2, a3);
%   }
%#v-
% results in:
%   fun(1)       %  --> (1, d2, *scratch*)
%   fun(1, 2)    %  --> (1, 2, *scratch*)
%   fun(1, 2, 3) %  --> (1, 2, 3)
% but
%   fun()        %  --> !!compulsory arg missing!!
%   fun(1, , )   %  --> (1, NULL, NULL)  !!empty args replaced with NULL!!
%\notes
% Never forget the _NARGS argument to \sfun{push_defaults}!
% 
% Mixed compulsory-optional arguments can be defined somewhat simpler, if
% the compulsory argument comes last:
%#v+
%   define fun2(a2) % (a1="d1", a2)
%   {
%      variable a1 = push_defaults("d1", _NARGS-1);
%      vmessage("(%S, %S)", a1, a2);
%   }
%#v-
% (To the author, the compulsory-first ordering appears more "natural",
% though. Maybe this is due to the Python experience where compulsory
% arguments need to precede optional ones.)
% 
% The arguments to push_defaults will always be evaluated. If time is an issue,
% use a placeholder (e.g. NULL) or a construct like
%#v+
%   define fun() % (a=time_consuming_fun())
%   {
%      !if (_NARGS)
%        time_consuming_fun();
%      variable a = ();
%      ...
%   }
%#v-
%\seealso{__push_args, __pop_args, _NARGS }
%!%-
define push_defaults() % args, n
{
   variable n = ();
   variable args = __pop_args(_NARGS-1);
   __push_args(args[[n:]]);
}

%!%+
%\function{prompt_for_argument}
%\synopsis{Prompt for an optional argument if it is not given.}
%\usage{Str prompt_for_argument(Ref prompt_function, [args], Int use_stack)}
%\description
%  This function facilitates the definition of function with optional
%  arguments.
%
%  The first argument is a prompt function (e.g. \sfun{read_mini} or
%  \sfun{read_with_completion}, followed by its arguments and the
%  \var{use_stack} argument.
%
%  If \var{use_stack} is non-zero, this function simply returns and
%  the calling code picks up the top element from stack.
%  Otherwise, \var{prompt_function} is called with the given arguments
%  (except when the minibuffer is already active, in which case an error
%  is risen).
%\example
%#v+
%  define prompt_for_message() % ([str])
%  {
%     variable str = prompt_for_argument(&read_mini,
%                                        "Message:", "", "", _NARGS);
%     message(str);
%  }
%#v-
%\seealso{push_defaults, _NARGS, read_mini, read_with_completion}
%!%-
define prompt_for_argument() % (fun, [args], use_stack)
{
   variable use_stack = ();
   variable args = __pop_args(_NARGS-2);
   variable fun = ();
   if (use_stack)
     return ();  % argument is already on stack
   else
     {
        if (MINIBUFFER_ACTIVE) % cannot use minibuffer for prompting
          error("missing argument");
        return @fun(__push_args(args));
     }
}

%!%+
%\function{push_array}
%\synopsis{Push an ordinary array on stack}
%\usage{(a[0], ..., a[-1])  push_array(Array a)}
%\description
% Push the elements of an array to the stack. This works like
% __push_args(args) but with an ordinary array (all types)
%\example
%#v+
%   variable a = ["message", "hello world"];
%   runhooks(push_array(a));
%#v-
%\notes
%   Elements of an Any_Type-array are references. They are dereferenced
%   in order to get type-independend behaviour.
%\seealso{array, pop2array, __push_args, __pop_args}
%!%-
define push_array(a)
{
   if (_typeof(a) == Any_Type)
        foreach (a)
             if (dup == NULL)
               ();
             else
              @();
   else
     foreach (a)
       ();
}

%!%+
%\function{get_blocal}
%\synopsis{Return value of blocal variable or default}
%\usage{Any get_blocal(String name, [Any default=NULL])}
%\description
% Deprecated: use the standard function \var{get_blocal_var} with a second
% argument.
% 
% This function is similar to get_blocal_var, but if the local variable
% "name" doesnot exist, it returns NULL or the default value instead 
% of an error.
%\example
% Since some time (which Jed version??), \var{get_blocal_var} also takes an
% optional default value:
%#v+
%    !if (get_blocal_var(foo), 0)
%      message("this buffer lacks foo");
%#v-
% prints the message if the blocal variable "foo" is zero or does not exist.
%\note
% There is no "default default" in \var{get_blocal_var}. Instead, it will
% throw an error, if there is no blocal_variable with \var{name} and no
% \var{default} argument is specified.
%\seealso{get_blocal_var, blocal_var_exists, set_blocal_var, define_blocal_var}
%!%-
define get_blocal() % (name, default=NULL)
{
   variable name, default;
   (name, default) = push_defaults( , NULL, _NARGS);

   if (blocal_var_exists(name))
     return get_blocal_var(name);
   return default;
}

%!%+
%\function{run_function}
%\synopsis{Run a function if it exists, return if fun is found.}
%\usage{Int_Type run_function(fun, [args])}
%\description
% Run a function (if it exists) pushing \var{args} as argument list.
% In contrast to \sfun{call_function}, there is a return value
% (pushed on the stack after the function call):
% 
%  1 the function was found (is_defined or internal)
%  0 the function was not found
%  
% The \var{fun} can be a function name or reference (this allows both:
% yet undefined functions (as string) as well as private or static functions
% (as reference).
%\example
%#v+
%
%    if (run_function("foo"))
%       message("\"foo\" successfull called");
%
%    !if (run_function(&foo))
%       message("\"foo\" is not defined");
%#v-
% The return value can be used to decide whether to take up a return value:
%#v+
%
%    if (run_function("filter", str))
%       str = ();
%#v-
%\notes
% If fun is (solely) an internal function, the optional arguments will be
% silently popped. If there are both, an internal and an intrinsic or library
% variant of a function, the non-internal takes precedence. Use \sfun{call} to
% explicitely call an internal function. 
%\seealso{call_function, runhooks, run_local_hook, call}
%!%-
define run_function()  % (fun, [args])
{
   variable args = __pop_args(_NARGS-1);
   variable fun = ();
   if (typeof(fun) == String_Type)
     {
        if (is_defined(fun) > 0)
          fun = __get_reference(fun);
        else if (is_internal(fun))
          {
             call(fun);
             return 1;
          }
     }
   if (typeof(fun) == Ref_Type)
     {
        @fun(__push_args(args));
        return 1;
     }
   return 0;
}

%!%+
%\function{contract_filename}
%\synopsis{Make a filename as short as possible without ambiguity}
%\usage{contract_filename(file, cwd=getcwd())}
%\description
%  The opposite of \sfun{expand_filename} (in some case of view)
%  Make a filename as short as possible without loss of information.
%  
%  * If the path starts with \var{cwd}, strip it.
%    (This maight fail on case insensitive filesystems).
%  * If the path starts with the home-dir, replace it with "~".
%\notes  
%  \sfun{expand_filname} will restore the original value.
%\seealso{expand_filename}
%!%-
define contract_filename() % (file, cwd=getcwd())
{
   variable file, cwd;
   (file, cwd) = push_defaults( , getcwd(), _NARGS);
   variable home = getenv("HOME");
   % strip leading cwd
   cwd = path_concat(cwd, ""); % ensure that cwd has a trailing dirsep
   if (is_substr(file, cwd) == 1)
     file = file[[strlen(cwd):]];
   % or replace HOME with ~
   else if (andelse{home != NULL}{strlen(home)})
     {
        home = path_concat(home, ""); % ensure home has a trailing dirsep
        if (is_substr(file, home) == 1)
          file = path_concat("~", file[[strlen(home):]]);
     }
   return file;
}

% when a buffer is folded what_line may give the false number
define what_line_if_wide ()
{
  if (count_narrows ())
    {
      push_narrow ();
      widen_buffer ();
      what_line ();
      pop_narrow ();
    }
  else
    what_line ();
}

%!%+
%\function{_implements}
%\synopsis{Create or reuse a new static namespace}
%\usage{_implements(Str name)}
%\description
%  The \sfun{_implements} function creates a new static namespace 
%  and associates it with the current compilation unit.
%  
%  If a namespace with the specified name already exists, the the current
%  static namespace is changed  to \var{name} with \sfun{use_namespace}.
%  
%  This alows re-evaluation of files in debugging|development mode.
%  
%\notes  
%  Since SLang 2 the standard implements() (re)uses a namespace if it was
%  defined in the same file. If defined in another file, it still throws a
%  namespace error.
%\example
%  To allow easy re-evaluation of a mode during development, write
%#v+
%   autoload("_implements", "sl_utils");
%   % other autoloads that should bind to the "Global" namespace
%   _implements("foo");
%   % autoloads, require, and function and variable definitions now go to the
%   % namespace "foo"
%#v-
%\seealso{implements, use_namespace}
%!%-
define _implements(name)
{
  if (length(where(name == _get_namespaces())))
    use_namespace(name);
  else
    implements(name);
}

%!%+
%\function{autoloads}
%\synopsis{Load functions from a file}
%\usage{autoloads(String funct, [String funct2 , ...], String file)}
%\description
% The `autoloads' function is used to declare a variable number of
% functions `funct' to the interpreter and indicate that they should be
% loaded from `file' when actually used.
% It does so by calling autoload(funct, file) for all \var{funct} arguments.
%\notes
% A future version of \sfun{autload} might provide for mulitple funct
% arguments and render \sfun{autoloads} obsolete.
% 
% _autoload(funct, file, funct2, file, ..., functN, file, N) is faster,
% (albeit less convenient to write and needing an explicit number argument).
% Use this for time-critical cases.
%\seealso{_autoload, autoload, require, evalfile}
%!%-
define autoloads(file) % (funct, [funct2 , ...], file)
{
   loop(_NARGS - 1) 
     autoload((), file);
}