When I use :set iskeyword? to view keyword characters, result for shell scripts is:


:h iskeyword says @ expands to any alpha character that the isalpha() functions returns true on, but what about 48-57 and 192-255 how can I view these characters?

2 Answers 2


You can use:

:echo map(range(48, 57), 'nr2char(v:val)')
:echo map(range(192, 255), 'nr2char(v:val)')

Which should output:

['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

['À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', '×', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'Þ', 'ß', 'à', 'á', 'â', 'ã', 'ä', 'å', ' æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', '÷', 'ø', 'ù ', 'ú', 'û', 'ü', 'ý', 'þ', 'ÿ']

To break it down:

  • range(192, 255) makes a list from 192 to 255.
  • nr2char() converts a number from its ASCII code to a string.
  • map() applies nr2char() to every item in the list.
  • Works great, thanks. I'm assuming the characters in range (192-255) are not normal ascii. is that some kind of "extended ascii"? Commented Apr 1, 2016 at 23:27
  • the_velor_fog: ASCII only has 7bit. Those are Unicode codepoints. You can go much higher than 255.
    – sapanoia
    Commented Nov 5, 2017 at 18:36
  • I find this helpful for working out the hex equivalent of decimal UTF notation utf8-chartable.de/unicode-utf8-table.pl?utf8=dec. Choose Englisch as the language
    – Steve
    Commented Jun 15, 2021 at 2:03
  • Actually, since I wrote this answer 5 years ago I also made a little CLI tool for this kind of stuff @Steve: github.com/arp242/uni; which, to me anyway, seems a lot easier than that website (but I'm probably a bit biased 🙃) You can even run it in your browser Commented Jun 15, 2021 at 2:38
  • @MartinTournoij oops didn't see the date :( . Ta for tool
    – Steve
    Commented Jun 15, 2021 at 23:32

I went a little overboard… turns out parsing the isfname format is hard!

This parses iskeyword into a single list of strings. Each string is either a single character (included) or a character preceded by ^ (excluded). Also, the strings isalpha() (included) and ^isalpha() (excluded) stand for all characters accepted by the C function.

You could shorten the range displays if you like with some tweaks. You could also put in join(',') and get a comma-separated list.

For example, after :setlocal iskeyword=@,48-57,_,192-255,-, we can do

:echo ParseIsFnameChars(&l:iskeyword)
['-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_', 'isalpha()', 'À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', '×', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'Þ', 'ß', 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ð', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', '÷', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'þ', 'ÿ']

Here's the function. Suggestions for improvement welcome 😅

" The format of this option is a list of parts, separated with commas.
" Each part can be a single character number or a range.  A range is two
" character numbers with '-' in between.  A character number can be a
" decimal number between 0 and 255 or the ASCII character itself (does
" not work for digits).  Example:
"        "_,-,128-140,#-43"      (include '_' and '-' and the range
"                                128 to 140 and '#' to 43)
" If a part starts with '^', the following character number or range
" will be excluded from the option.  The option is interpreted from left
" to right.  Put the excluded character after the range where it is
" included.  To include '^' itself use it as the last character of the
" option or the end of a range.  Example:
"        "^a-z,#,^"      (exclude 'a' to 'z', include '#' and '^')
" If the character is '@', all characters where isalpha() returns TRUE
" are included.  Normally these are the characters a to z and A to Z,
" plus accented characters.  To include '@' itself use "@-@".  Examples:
"        "@,^a-z"        All alphabetic characters, excluding lower
"                        case ASCII letters.
"        "a-z,A-Z,@-@"   All letters plus the '@' character.
" A comma can be included by using it where a character number is
" expected.  Example:
"        "48-57,,,_"     Digits, comma and underscore.
" A comma can be excluded by prepending a '^'.  Example:
"        " -~,^,,9"      All characters from space to '~', excluding
"                        comma, plus <Tab>.
" See |option-backslash| about including spaces and backslashes.

function ParseIsFnameChars(value) abort
  const parts = split(a:value, ',', v:true)
  const num_parts = len(parts)
  let result = []
  let index = 0
  let last_caret = v:false
  for part in parts
    let part_to_parse = substitute(part, '^\^', '', '')
    let part_ends = split(part_to_parse, '-')
    let num_ends = len(part_ends)
    if part_to_parse is# '-' " => num_ends is# 0
      " just -
      let parsed = ['-']
    elseif empty(part_to_parse) " => num_ends is# 0
      let parsed = [',']
    elseif num_ends is# 1
      " single items
      let parsed = [part_to_parse =~# '\d\+'
            \ ? str2nr(part_to_parse)->nr2char()
            \ : part_to_parse is# '@'
            \ ? 'isalpha()'
            \ : part_to_parse]
    elseif num_ends is# 2 && part is# '@-@'
      let parsed = ['@']
    elseif num_ends is# 2 " && part isnot# '@-@'
      let left_end_parsed = part_ends[0] =~# '\d\+'
            \ ? str2nr(part_ends[0])
            \ : char2nr(part_ends[0])
      let right_end_parsed = part_ends[1] =~# '\d\+'
            \ ? str2nr(part_ends[1])
            \ : char2nr(part_ends[1])
      let parsed = range(left_end_parsed, right_end_parsed)->map({_, v -> nr2char(v)})
      throw "ParseIsFnameChars: ".part." is not formatted correctly: wrong number of subparts to -"
    if part =~# '^\^$' && index is# num_parts-1
      " include a '^'
      let result += ['^']
    elseif part =~# '^\^$' " && index isnot# num_parts-1
      " preceding a comma
      let last_caret = v:true
    elseif last_caret
      if empty(part)
        call filter(result, {_, v -> v isnot# ','})
        let result += ['^,']
        throw "ParseIsFnameChars: ".part." is not formatted correctly: non-comma after non-ending ^"
      let last_caret = v:false
    elseif part =~# '^\^'
      " exclude
      call filter(result, {_, v -> index(parsed, v) >= 0})
      let result += map(parsed, '"^".v:val')
      " include
      let to_remove = mapnew(parsed, '"^".v:val')
      call filter(result, {_, v -> index(to_remove, v) < 0})
      let result += parsed
    let index += 1
  return result->sort()->uniq()

Martin and I had the following conversation in chat that I thought was worth preserving:

Martin Tournoij

@D.BenKnoble Re: https://vi.stackexchange.com/a/31564/51 Good grief ... That's all I have to say about that 🙃 Actually, I do have some more to say: something like a keyword() function wouldn't be a bad idea; shouldn't be too hard to add to Vim That's what I would have done anyway, usually patches like this should get accepted fairly quickly And seems like a useful function to have e.g. keyword() → returns all keyword characters as an array Actually seems less effort than that Vim function heh, and the Vim codebase actually isn't as bad as some people make it out to be; some parts are hairy, but a new function is something even a bad C programmer like me can do :-) as long as you stay away from the whole screen update stuff and such

D. Ben Knoble

@MartinTournoij Honestly the function is pretty inefficient because of all the list stuff. Worse though is that it’s not enough to just include all the keyword characters. For one, there’s @ for isalpha; for two, you can exclude characters. I think it would need to turn more information (possibly a dict?). The good news is the vim codebase should already have some logic for parsing iskeyword. (I also may have spotted a bug in my code lol)

Martin Tournoij

It's init_chartab() in charset.c, but right now it just sets the values on the buffer directly rather than parsing it in to a struct or some such and using that to set the buffer values, so that would need some rewriting I thought I might cook up a quick patch, but this is too much work 🙃 Especially since I don't really have a use case for this myself

D. Ben Knoble

For anyone else: https://github.com/vim/vim/blob/4781d6fd8670af415c3b78f00d70036af85bd286/src/charset.c#L150

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.