Commits

ZyX_I committed 9967b3f

Add frawor-od-override

Comments (0)

Files changed (7)

autoload/frawor/options.vim

 "▶1 Header
 scriptencoding utf-8
-execute frawor#Setup('0.0', {'@/checks': '0.0'})
+execute frawor#Setup('0.1', {'@/checks': '0.0'})
 "▶1 Define messages
 if v:lang=~?'ru'
     let s:_messages=map({
                 \               'префиксом переменной',
             \}, '"Ошибка получения настройки для дополнения %s: ".v:val')
     call extend(s:_messages, map({
-                \  'nooptions': 'глобальная переменная не содержит '.
-                \               'описания настроек (_options)',
-                \'_optnotdict': 'значение ключа _messages не является словарём',
-                \   'nooption': 'отсутствует описание настройки',
-                \ 'optnotdict': 'описание настройки не является словарём',
-                \ 'scopesnstr': 'ключ «scopes» описания настройки должен '.
-                \               'быть строкой',
-                \  'invscopes': 'строка «%s» не является правильным '.
-                \               'описанием локальности настройки',
-                \'scopesanstr': 'второй аргумент должен быть строкой',
-                \ 'invscopesa': 'строка «%s» не является правильным '.
-                \               'описанием локальности настройки',
-                \    'chkfail': 'проверка настройки провалилась '.
-                \               '(настройка была получена из переменной %s)',
-                \    'filfail': 'фильтрация настройки провалилась '.
-                \               '(настройка была получена из переменной %s)',
-                \    'onotdef': 'настройка нигде не определена',
-                \ 'typenmatch': 'тип настройки, полеченной из переменной %s, '.
-                \               'не совпадает с типом настройки, '.
-                \               'полученной ранее',
-                \  'mergefref': 'невозможно слить две ссылки на функции',
-                \    'umerger': 'неверное значение ключа «merger»',
+                \   'nooptions': 'глобальная переменная не содержит '.
+                \                'описания настроек (_options)',
+                \ '_optnotdict': 'значение ключа _messages не является '.
+                \                'словарём',
+                \    'nooption': 'отсутствует описание настройки',
+                \  'optnotdict': 'описание настройки не является словарём',
+                \  'scopesnstr': 'ключ «scopes» описания настройки должен '.
+                \                'быть строкой',
+                \   'invscopes': 'строка «%s» не является правильным '.
+                \                'описанием локальности настройки',
+                \'overridenstr': 'ключ «override» описания настройки должен '.
+                \                'быть строкой',
+                \ 'invoverride': 'строка «%s» не является правильным '.
+                \                'описанием области видимости',
+                \ 'scopesanstr': 'второй аргумент должен быть строкой',
+                \  'invscopesa': 'строка «%s» не является правильным '.
+                \                'описанием локальности настройки',
+                \     'chkfail': 'проверка настройки провалилась '.
+                \                '(настройка была получена из переменной %s)',
+                \     'filfail': 'фильтрация настройки провалилась '.
+                \                '(настройка была получена из переменной %s)',
+                \     'onotdef': 'настройка нигде не определена',
+                \  'typenmatch': 'тип настройки, полеченной из переменной %s, '.
+                \                'не совпадает с типом настройки, '.
+                \                'полученной ранее',
+                \   'mergefref': 'невозможно слить две ссылки на функции',
+                \     'umerger': 'неверное значение ключа «merger»',
             \}, '"Ошибка получения настройки %s для дополнения %s: ".v:val'))
 else
     let s:_messages=map({
                 \ 'invoprefix': 'string `%s'' is not a valid option prefix',
             \}, '"Error while obtaining option for plugin %s: ".v:val')
     call extend(s:_messages, map({
-                \  'nooptions': 'global variable does not contain _options',
-                \'_optnotdict': '_messages key value is not a Dictionary',
-                \   'nooption': 'option description is missing',
-                \ 'optnotdict': 'option description is not a Dictionary',
-                \ 'scopesnstr': '`scopes'' key must have a String value',
-                \  'invscopes': "string `%s' is not a valid scopes description",
-                \'scopesanstr': 'second argument is not a String',
-                \ 'invscopesa': "string `%s' is not a valid scopes description",
-                \    'chkfail': 'option failed to pass a check '.
-                \               '(option was obtained from %s variable)',
-                \    'filfail': 'option filtering failed'.
-                \               '(option was obtained from %s variable)',
-                \    'onotdef': 'option was not defined anywhere',
-                \ 'typenmatch': 'type of the option obtained from %s variable '.
-                \               'does not match type of the option obtained '.
-                \               'earlier',
-                \  'mergefref': 'unable to merge function references',
-                \    'umerger': '`merger'' key value is not valid',
+                \   'nooptions': 'global variable does not contain _options',
+                \ '_optnotdict': '_messages key value is not a Dictionary',
+                \    'nooption': 'option description is missing',
+                \  'optnotdict': 'option description is not a Dictionary',
+                \  'scopesnstr': '`scopes'' key must have a String value',
+                \   'invscopes': "string `%s' is not a valid scopes ".
+                \                "description",
+                \'overridenstr': '`override'' key must have a String value',
+                \ 'invoverride': "string `%s' is not a valid scopes ".
+                \                "description",
+                \ 'scopesanstr': 'second argument is not a String',
+                \  'invscopesa': "string `%s' is not a valid scopes ".
+                \                "description",
+                \     'chkfail': 'option failed to pass a check '.
+                \                '(option was obtained from %s variable)',
+                \     'filfail': 'option filtering failed'.
+                \                '(option was obtained from %s variable)',
+                \     'onotdef': 'option was not defined anywhere',
+                \  'typenmatch': 'type of the option obtained from %s '.
+                \                'variable does not match type of the option '.
+                \                'obtained earlier',
+                \   'mergefref': 'unable to merge function references',
+                \     'umerger': '`merger'' key value is not valid',
             \}, '"Error while obtaining option %s for plugin %s: ".v:val'))
 endif
 "▶1 getovalue  :: oshadow ovalue, oid, plid, ovar → ovalue
     endif
     return eval(s:merges[type(a:a)])
 endfunction
+"▶1 mergeopts :: oshadow, od, oid, plid, ovar → od
+function s:F.mergeopts(oshadow, ovalue, od, oid, plid, ovar)
+    if a:ovar is# 'default'
+        let a:od.newval=a:ovalue
+    else
+        let a:od.newval=s:F.getovalue(a:oshadow, a:ovalue, a:oid, a:plid,
+                    \                 a:ovar)
+    endif
+    try
+        if exists('a:od.ovalue')
+            if a:oshadow.override is 0 ||
+                        \a:od.mergedin!~#'['.a:oshadow.override.']'
+                let a:od.tmp=a:od.ovalue
+                try
+                    let a:od.ovalue=a:oshadow.merger(a:od.tmp, a:od.newval,
+                                \                    a:oid, a:plid, a:ovar)
+                finally
+                    unlet a:od.tmp
+                endtry
+            endif
+        else
+            let a:od.ovalue=deepcopy(a:od.newval)
+        endif
+    finally
+        unlet a:od.newval
+    endtry
+    let a:od.mergedin=get(a:od, 'mergedin', '').a:ovar[0]
+    return a:od
+endfunction
 "▶1 getoption  :: {f}, oid[, scopes] + p:_options, p:_oprefix → ovalue
 let s:dochecks=1
+let s:scopereg='^[wtbg]\+$'
 function s:F.getoption(plugdict, fdict, oid, ...)
     "▶2 Check arguments
     if s:dochecks
         if has_key(option, 'scopes')
             if type(option.scopes)!=type('')
                 call s:_f.throw('scopesnstr', a:oid, a:plugdict.id)
-            elseif option.scopes!~#'^[wtbg]\+$'
+            elseif option.scopes!~#s:scopereg
                 call s:_f.throw('invscopes', a:oid,a:plugdict.id,option.scopes)
             endif
             let oshadow.scopes=option.scopes
         else
             let oshadow.scopes='bg'
         endif
+        "▶3 `override' key
+        if has_key(option, 'override')
+            if type(option.override)!=type('')
+                call s:_f.throw('overridenstr', a:oid, a:plugdict.id)
+            elseif option.override!~#s:scopereg
+                call s:_f.throw('invoverride', a:oid, a:plugdict.id,
+                            \                  option.override)
+            endif
+            let oshadow.override=option.override
+        else
+            let oshadow.override=0
+        endif
         "▶3 `merger' key
         if has_key(option, 'merger')
             if type(option.merger)==2 && exists('*option.merger')
     if a:0
         if type(a:1)!=type('')
             call s:_f.throw('scopesanstr', a:oid, a:plugdict.id)
-        elseif a:1!~#'^[wtbg]\+$'
+        elseif a:1!~#s:scopereg
             call s:_f.throw('invscopesa', a:oid, a:plugdict.id, a:1)
         endif
         let scopes=a:1
             let soptions=eval(ovar)
             if type(soptions)==type({}) && has_key(soptions, a:oid)
                 if merge
-                    let d.newval=s:F.getovalue(oshadow, soptions[a:oid], a:oid,
-                                \              a:plugdict.id, ovar)
-                    if exists('d.ovalue')
-                        let d.tmp=d.ovalue
-                        let d.ovalue=oshadow.merger(d.tmp, d.newval, a:oid,
-                                    \               a:plugdict.id, ovar)
-                        unlet d.tmp
-                    else
-                        let d.ovalue=deepcopy(d.newval)
-                    endif
-                    unlet d.newval
+                    let d=s:F.mergeopts(oshadow, soptions[a:oid], d, a:oid,
+                                \       a:plugdict.id, ovar)
                 else
                     return s:F.getovalue(oshadow, soptions[a:oid], a:oid,
                                 \        a:plugdict.id, ovar)
             let ovar=scope.':'.oprefix.'_'.a:oid
             if exists(ovar)
                 if merge
-                    let d.newval=s:F.getovalue(oshadow, eval(ovar), a:oid,
-                                \              a:plugdict.id, ovar)
-                    if exists('d.ovalue')
-                        let d.tmp=d.ovalue
-                        let d.ovalue=oshadow.merger(d.tmp, d.newval, a:oid,
-                                    \               a:plugdict.id, ovar)
-                        unlet d.tmp
-                    else
-                        let d.ovalue=deepcopy(d.newval)
-                    endif
-                    unlet d.newval
+                    let d=s:F.mergeopts(oshadow, eval(ovar), d, a:oid,
+                                \       a:plugdict.id, ovar)
                 else
-                    return s:F.getovalue(oshadow, eval(ovar), a:oid, 
+                    return s:F.getovalue(oshadow, eval(ovar), a:oid,
                                 \        a:plugdict.id, ovar)
                 endif
             endif
     "▶2 `default' key
     if has_key(option, 'default')
         if merge
-            let d.newval=option.default
-            if exists('d.ovalue')
-                let d.tmp=d.ovalue
-                let d.ovalue=oshadow.merger(d.tmp, d.newval, a:oid,
-                            \               a:plugdict.id, 'default')
-            else
-                let d.ovalue=deepcopy(d.newval)
-            endif
+            let d=s:F.mergeopts(oshadow, option.default, d, a:oid, a:plugdict.id,
+                        \       'default')
             return d.ovalue
         else
             return option.default
         Dictionary which describes option. It has the following keys:
         Key         Description ~
                                                            *frawor-od-default*
-        default     Determines default value of an option. Is neither checker 
+        default     Determines default value of an option. Is neither checked 
                     nor filtered.
                                                             *frawor-od-scopes*
         scopes      Scope of variables which can be searched for an option. 
                     Note 2: if merger key is present, first of merged options 
                     is deep copied (see |deepcopy()|). Otherwise copying is 
                     not performed.
+                                                          *frawor-od-override*
+        override    Determines values from which scopes sholud override 
+                    default value. Value is the same as |frawor-od-scopes|.
+                    This option is ignored unless |frawor-od-merger| is 
+                    defined.
 
 funcdescr : {}                                            *frawor-t-funcdescr*
         Function description is a dictionary with the following keys (all 
 changes.
 
 @frawor:
-    0.1: Added |frawor-f-require| and |frawor-fk-depadd|
-    0.2: Added s:_loading variable
+    0.1: Added |frawor-f-require| and |frawor-fk-depadd|.
+    0.2: Added s:_loading variable.
     0.3: Added isftplugin key to |frawor-t-plugdict|,
          adjusted the way plugin type is determined.
     1.0: Removed twoload feature and s:_loading variable.
     0.1: Added possibility to specify dictionaries in `strfunc' and `func' 
          keys.
 @/table:
-    0.1: Posted |frawor-r-strdisplaywidth|
+    0.1: Posted |frawor-r-strdisplaywidth|.
 @/fwc:
-    0.1: (intfuncs-0.1) Added |FWC-c-idof|
-    0.2: (intfuncs-0.2) Made |FWC-f-path| filter expand argument
-    0.3: (intfuncs-0.3) Added -onlystrings support for |FWC-c-range|
+    0.1: (intfuncs-0.1) Added |FWC-c-idof|.
+    0.2: (intfuncs-0.2) Made |FWC-f-path| filter expand argument.
+    0.3: (intfuncs-0.3) Added -onlystrings support for |FWC-c-range|.
     0.4: (intfuncs-0.4) Made |FWC-f-isfunc| transform argument to a function 
-                        reference
+                        reference.
 @/fwc/constructor:
     0.1: Added more functions
     1.0: Renamed some functions so that they now have “_” at the start of 
     4.2: Made it possible to minimize code (currently only indent is removed 
          and command names are truncated).
 @/os:
-    0.1: Added |frawor-r-os.path.relpath|
-         Made |frawor-r-os.path.normpath| also simplify its argument
+    0.1: Added |frawor-r-os.path.relpath|.
+         Made |frawor-r-os.path.normpath| also simplify its argument.
     0.2: Added |frawor-r-os.readsystem|, made |frawor-r-os.run| also accept 
-         plain strings as the first argument
+         plain strings as the first argument.
 @/decorators/altervars:
-    0.1: Added folds |frawor-av-special|
+    0.1: Added folds |frawor-av-special|.
 @/python:
     1.0: Removed _r.py resource, made it handle python and python3 separately.
 @/functions:
     0.1: Added ability to have [{plid}, {version}, {key}] in 
          |frawor-fd-function| and |frawor-fd-thisplugin|.
 @/commands:
-    0.1: Added usedictcompsplitfunc option
+    0.1: Added usedictcompsplitfunc option.
+@/options:
+    0.1: Added |frawor-od-override|.
 
 vim: ft=help:tw=78

test/getoption.ok

 ('+'): 'bg'
 ('a'): ['default', 'gvar', 'gdict', 'bvar', 'bdict', 'tvar', 'tdict', 'wvar']
 ('F'): function('string')
-('F', 'b'): function('tr')
+('F', 'b'): function('tr')
+('b'): '1'
+('b'): 'b1'
+('b'): 'bg'

test/invalid-getoption.ok

 .19: autoload/frawor/options:filfail
 checker: ['abc']
 .20: autoload/frawor/options:chkfail
-.21: autoload/frawor/options:mergefref
+.21: autoload/frawor/options:mergefref
+.22: autoload/frawor/options:overridenstr
+.23: autoload/frawor/options:invoverride

test/rtp/plugin/getoption.vim

             \      'merger': s:F.merger,
             \     'default': ['default']},
             \'F': {'default': function("tr")},
+            \'b': {'default': '1',
+            \      'merger': 'extend',
+            \      'override': 'g',},
         \}
 function s:F.writeoption(...)
     call WriteFile('('.string(a:000)[1:-2].'): '.
 let g:getoptionOptions.F=function('string')
 call s:F.testoptions(['F'])
 call s:F.testoptions(['F', 'b'])
+
+call s:F.testoptions(['b'])
+let b:getoption_b='b'
+call s:F.testoptions(['b'])
+let g:getoption_b='g'
+call s:F.testoptions(['b'])

test/rtp/plugin/invalid-getoption.22.vim

+execute frawor#Setup('0.0', {'autoload/frawor/options': '0.0'})
+let s:_options={"test": {'override': 0}}
+call s:_f.getoption("test")

test/rtp/plugin/invalid-getoption.23.vim

+execute frawor#Setup('0.0', {'autoload/frawor/options': '0.0'})
+let s:_options={"test": {'override': 'a'}}
+call s:_f.getoption("test")