Commits

ZyX_I  committed 7497d20

Added `newfeature' feature, made other features use it in order to register

  • Participants
  • Parent commits 55425fe

Comments (0)

Files changed (2)

File doc/frawor.txt

 ------------------------------------------------------------------------------
 2.1. Functions                                              *frawor-functions*
 
-                                                           *frawor-f-Register*
                                                             *FraworRegister()*
 FraworRegister(version, sid, file, dependencies, oneload, g, F)
         Registers plugin sourced from {file} that has given {version} (see 
 version :: [ Integer ]                                      *frawor-t-version*
         List of integers, all integers must be nonnegative.
 
+==============================================================================
+5. Features                                                  *frawor-features*
+
+warn : function ({msgid}, ...) → {message}                     *frawor-f-warn*
+        Echoes message `Frawor:{plid}:{message}' where {message} is obtained by 
+        getting {msgid} out of `s:g._messages' (s:g is a dictionary that is 
+        passed as last but one argument to FraworRegister). If more then one 
+        argument is present, then {message} and that arguments will be passed to 
+        |printf()|. Warn function return formatted message. {plid} is an id of 
+        a plugin that uses this function. Semicolon and backslash in {plid} will 
+        be escaped.
+throw : function ({msgid}, ...) → exception                   *frawor-f-throw*
+        Calls |frawor-f-warn| and throws an exception with text 
+        `Frawor:{plid}:{msgid}:{message}' where {message} is a value returned by 
+        |frawor-f-warn|. Semicolon and backslash in {plid} and {msgid} will be 
+        escaped.
+newfeature : function ({fid}, {fopts})
+        Registers plugin feature with id {fid} ({fid} must contain only digits, 
+        latin letters and underscores). {fopts} is a dictionary with the 
+        following keys (all are optional, but feature without any keys is 
+        useless):
+        Key     Description ~
+        cons    Reference to a function that will take |frawor-t-plugdict| as 
+                its first argument and, possibly, some additional arguments. It 
+                is just a normal function that can do anything related to the 
+                plugin that uses frawor. When plugin is registered, function 
+                that is a wrapper to this function will be added to s:F._frawor 
+                dictionary.
+        load    Reference to a function that will take |frawor-t-plugdict| as 
+                its first argument. This function will be called only once for 
+                each plugin that depends on the plugin that registers this 
+                feature. Function will be called when dependent plugin is 
+                loaded. If value returned by this function is not 0, then it 
+                will be assigned to plugdict.features[{fid}]
+        unload  Reference to a function that will take |frawor-t-plugdict| as 
+                its first argument and plugdict.features[{fid}] as a second. 
+                This function will be called only once for each plugin that 
+                depends on the plugin that registers this feature. Function will 
+                be called when dependent plugin is unloaded.
+        init    Any value. Deep copy (|deepcopy()|) of it will be assigned to 
+                plugdict.features[{fid}]. Note that if `load' key is present, 
+                then this key will be ignored.
+
 vim: ft=help:tw=78

File plugin/frawor.vim

 let s:g.pls={} " Plugin dictionaries
 let s:g.loading={}
 let s:g.features={}
-let s:g.dependents={}
+let s:g.dependents={'plugin/frawor': {}}
 "▶2 Messages
 if v:lang=~?'ru'
     let s:g._messages={
                 \                'не содержит сообщений (_messages)',
                 \   'nomessage': 'Список сообщений дополнения %s '.
                 \                'не содержит сообщения %s',
+                \   'mesnotstr': 'Сообщение дополнения %s не является строкой',
+                \   'fidnotstr': 'Ошибка регистрации возможности '.
+                \                'дополнения %s: название возможности '.
+                \                'не является строкой',
+                \   'fidinvstr': 'Ошибка регистрации возможности '.
+                \                'дополнения %s: строка «%s» '.
+                \                'не может являться названием возможности',
+                \     'featreg': 'Ошибка регистрации возможности %s '.
+                \                'дополнения %s: возможность с таким именем '.
+                \                'уже зарегестрирована дополнением %s',
+                \ 'foptsnotdct': 'Ошибка регистрации возможности %s '.
+                \                'дополнения %s: описание возможности '.
+                \                'не является словарём',
+                \   'consnfref': 'Ошибка регистрации возможности %s '.
+                \                'дополнения %s: значение ключа «cons» '.
+                \                'не является ссылкой на функцию',
+                \   'consncall': 'Ошибка регистрации возможности %s '.
+                \                'дополнения %s: значение ключа «cons» '.
+                \                'не может быть вызвано (возможно '.
+                \                'вы пытались создать ссылку на внутренную '.
+                \                'функцию дополнения без раскрытия «s:» в '.
+                \                '«<SNR>_{N}»?)',
+                \   'loadnfref': 'Ошибка регистрации возможности %s '.
+                \                'дополнения %s: значение ключа «load» '.
+                \                'не является ссылкой на функцию',
+                \   'loadncall': 'Ошибка регистрации возможности %s '.
+                \                'дополнения %s: значение ключа «load» '.
+                \                'не может быть вызвано (возможно '.
+                \                'вы пытались создать ссылку на внутренную '.
+                \                'функцию дополнения без раскрытия «s:» в '.
+                \                '«<SNR>_{N}»?)',
             \}
 else
     let s:g._messages={
                 \  'nomessages': '%s global variable does not contain '.
                 \                '_messages',
                 \   'nomessage': '%s messages does not contain message id %s',
+                \   'mesnotstr': '%s message is not a String',
+                \   'fidnotstr': 'Error while registering feature '.
+                \                'of a plugin %s: feature ID is not a String',
+                \   'fidinvstr': 'Error while registering feature '.
+                \                'of a plugin %s: string `%s'' '.
+                \                'is not a valid feature ID',
+                \     'featreg': 'Error while registering feature %s '.
+                \                'of a plugin %s: feature with such ID was '.
+                \                'already registered by plugin %s',
+                \ 'foptsnotdct': 'Error while registering feature %s '.
+                \                'of a plugin %s: description argument '.
+                \                'is not a Dictionary',
+                \   'consnfref': 'Error while registering feature %s '.
+                \                'of a plugin %s: `cons'' fopts key is not '.
+                \                'a function reference',
+                \   'consncall': 'Error while registering feature %s '.
+                \                'of a plugin %s: `cons'' fopts key is not '.
+                \                'callable (perhaps you tried to create '.
+                \                'a reference to a script-local function '.
+                \                'without replacing `s:'' with `<SNR>_{N})''?)',
+                \   'loadnfref': 'Error while registering feature %s '.
+                \                'of a plugin %s: `load'' fopts key is not '.
+                \                'a function reference',
+                \   'loadncall': 'Error while registering feature %s '.
+                \                'of a plugin %s: `load'' fopts key is not '.
+                \                'callable (perhaps you tried to create '.
+                \                'a reference to a script-local function '.
+                \                'without replacing `s:'' with `<SNR>_{N})''?)',
             \}
 endif
 "▶1 s:Eval
     "▶2 Constructing plugdict
     "▶3 Some trivial construction
     let plugdict={
-                \        'type': plugtype,
-                \          'id': plid,
-                \ 'runtimepath': plrtp,
-                \     'version': plversion,
-                \     'oneload': !!a:oneload,
-                \        'file': a:file,
-                \         'sid': a:sid,
-                \'dependencies': {'plugin/frawor': 1},
-                \      'status': 1,
-                \    'features': {},
-                \           'F': a:F,
-                \           'g': a:g,
-                \   'intprefix': 's:g.pls['.
-                \                   substitute(string(plid), '\n', '''."\n".''',
-                \                              'g').']',
-                \    'provides': {},
+                \         'type': plugtype,
+                \           'id': plid,
+                \  'runtimepath': plrtp,
+                \      'version': plversion,
+                \      'oneload': !!a:oneload,
+                \         'file': a:file,
+                \          'sid': a:sid,
+                \ 'dependencies': {'plugin/frawor': 1},
+                \       'status': 1,
+                \     'features': {},
+                \            'F': a:F,
+                \            'g': a:g,
+                \    'intprefix': 's:g.pls['.
+                \                    substitute(string(plid),'\n', '''."\n".''',
+                \                               'g').']',
+                \'addedfeatures': {},
             \}
     let plugdict.F._frawor={}
     "▶3 Processing dependencies
         endif
         let s:g.dependents[dplid][plid]=1
     endfor
+    let s:g.dependents['plugin/frawor'][plid]=1
     lockvar! plugdict.dependencies
     "▶2 Locking plugdict
     lockvar 1 plugdict
 endfunction
 "▶1 addfeature
 function s:F.addfeature(plugdict, feature)
+    if has_key(a:plugdict.addedfeatures, a:feature.id)
+        return 0
+    endif
     "▶2 Feature provides constructed function
     if has_key(a:feature, 'cons')
         execute  'function a:plugdict.F._frawor.'.a:feature.id."(...)\n".
     elseif has_key(a:feature, 'init')
         let a:plugdict.features[a:feature.id]=deepcopy(a:feature.init)
     endif
+    let a:plugdict.addedfeatures[a:feature.id]=1
     "▲2
     return a:feature
 endfunction
                 else
                     call s:F._frawor.throw('reqfailed', dplid, a:plid)
                 endif
-                call map(values(s:g.pls[dplid].provides),
-                            \'s:F.addfeature(plugdict, v:val)')
+                if has_key(s:g.pls[dplid].features, 'newfeature')
+                    call map(values(s:g.pls[dplid].features.newfeature),
+                                \'s:F.addfeature(plugdict, v:val)')
+                endif
             endfor
-            lockvar 1 plugdict.features
             "▲2
             if !plugdict.oneload
                 execute 'source '.fnameescape(plugdict.file)
             let plugdict.status=2
             lockvar! plugdict.status
             "▶2 Adding features to already loaded plugins
-            if has_key(s:g.dependents, a:plid)
+            if has_key(s:g.dependents, a:plid) &&
+                        \has_key(plugdict.features, 'newfeature')
                 for rplugdict in map(keys(s:g.dependents[a:plid]),
                             \        's:g.pls[v:val]')
-                    call map(values(plugdict.provides),
+                    call map(values(plugdict.features.newfeature),
                                 \'s:F.addfeature(rplugdict, v:val)')
                 endfor
             endif
 function FraworRegister(...)
     return call(s:F.newplugin, a:000, {})
 endfunction
-"▶1 features.warn
-let s:g.features.warn={
+"▶1 isfunc
+function s:F.isfunc(Func, msgpref, fid, plid)
+    if type(a:Func)!=2
+        call s:F._frawor.throw(a:msgpref.'nfref', a:fid, a:plid)
+    elseif !exists('*a:Func')
+        call s:F._frawor.throw(a:msgpref.'ncall', a:fid, a:plid)
+    endif
+endfunction
+"▶1 features.newfeature.cons
+let s:g.features.newfeature={
             \'plid': 'plugin/frawor',
-            \  'id': 'warn',
+            \  'id': 'newfeature',
+            \'init': {},
         \}
-function s:g.features.warn.cons(plugdict, msgid, ...)
+function s:g.features.newfeature.cons(plugdict, fid, fopts)
+    if type(a:fid)!=type("")
+        call s:F._frawor.throw('fidnotstr', a:plugdict.id)
+    elseif empty(a:fid) || a:fid=~#'\W'
+        call s:F._frawor.throw('fidinvstr', a:plugdict.id, a:fid)
+    elseif has_key(s:g.features, a:fid)
+        call s:F._frawor.throw('featreg', a:fid, a:plugdict.id,
+                    \                     s:g.features[a:fid].plid)
+    elseif type(a:fopts)!=type({})
+        call s:F._frawor.throw('foptsnotdct', a:fid, a:plugdict.id)
+    endif
+    let feature={}
+    let feature.plid=a:plugdict.id
+    let feature.id=a:fid
+    if has_key(a:fopts, 'cons')
+        call s:F.isfunc(a:fopts.cons, 'cons', a:fid, feature.plid)
+        let feature.cons=a:fopts.cons
+    endif
+    if has_key(a:fopts, 'load')
+        call s:F.isfunc(a:fopts.load, 'load', a:fid, feature.plid)
+        let feature.load=a:fopts.load
+    endif
+    let a:plugdict.features.newfeature[a:fid]=feature
+    let s:g.features[a:fid]=feature
+    if has_key(s:g.dependents, a:plugdict.id)
+        call map(keys(s:g.dependents[a:plugdict.id]),
+                    \'s:F.addfeature(s:g.pls[v:val], feature)')
+    endif
+endfunction
+"▶1 features.newfeature.unload
+function s:g.features.newfeature.unload(plugdict, featdict)
+    for fid in keys(a:featdict)
+        unlet s:g.features[fid]
+    endfor
+endfunction
+"▶1 warn
+function s:F.warn(plugdict, msgid, ...)
     if !has_key(a:plugdict.g, '_messages')
         call s:F._frawor.throw('nomessages', a:plugdict.id)
+    elseif type(a:msgid)!=type("")
+        call s:F._frawor.throw('mesnotstr', a:plugdict.id)
     elseif !has_key(a:plugdict.g._messages, a:msgid)
         call s:F._frawor.throw('nomessage', a:plugdict.id, a:msgid)
     endif
     if a:0
         let message=call('printf', [message]+a:000)
     endif
-    let message='Frawor:'.a:plugdict.id.':'.message
     echohl ErrorMsg
-    echomsg message
+    echomsg 'Frawor:'.escape(a:plugdict.id, '\:').':'.message
     echohl None
-    return 0
+    return message
 endfunction
-"▶1 features.throw
-let s:g.features.throw={
-            \'plid': 'plugin/frawor',
-            \  'id': 'throw',
-        \}
-function s:g.features.throw.cons(plugdict, msgid, ...)
-    call call(s:g.features.warn.cons, [a:plugdict, a:msgid]+a:000, {})
-    let throwmsg='Frawor:'.a:plugdict.id.':'.a:msgid
+"▶1 throw
+function s:F.throw(plugdict, msgid, ...)
+    let throwmsg='Frawor:'.escape(a:plugdict.id, '\:').':'.
+                \escape(a:msgid, '\:').':'.
+                \call(s:F.warn, [a:plugdict, a:msgid]+a:000, {})
     throw throwmsg
 endfunction
 "▶1 Plugin registration
 call s:F.newplugin([0, 0], s:Eval('+matchstr(expand("<sfile>"), ''\d\+'')'),
             \      expand('<sfile>:p'), {}, 1, s:g, s:F)
-"▶1 XXX adding missing features
-let s:plugdict=s:g.pls['plugin/frawor']
-let s:plugdict.provides.warn=s:g.features.warn
-let s:plugdict.provides.throw=s:g.features.throw
-call s:F.addfeature(s:plugdict, s:g.features.warn)
-call s:F.addfeature(s:plugdict, s:g.features.throw)
-unlet s:plugdict
+"▶2 XXX adding missing features
+let s:g.pls['plugin/frawor'].features.newfeature={
+            \   'newfeature': s:g.features.newfeature,
+        \}
+call s:F.addfeature(s:g.pls['plugin/frawor'], s:g.features.newfeature)
+call s:F._frawor.newfeature('warn', {'cons': s:F.warn})
+call s:F._frawor.newfeature('throw', {'cons': s:F.throw})
 
 " vim: fmr=▶,▲ sw=4 ts=4 sts=4 et tw=80