keflavich / macvim-skim

Interlink macvim & skim for an integrated LaTeX DE
16 stars 6 forks source link

Deal with filenames that might be subsets of other open filenames #2

Open CptStubing opened 11 years ago

CptStubing commented 11 years ago

According to :help bufname()

bufname({expr}) *bufname()* The result is the name of a buffer, as it is displayed by the ":ls" command. If {expr} is a Number, that buffer number's name is given. Number zero is the alternate buffer for the current window. If {expr} is a String, it is used as a |file-pattern| to match with the buffer names. This is always done like 'magic' is set and 'cpoptions' is empty. When there is more than one match an empty string is returned.

Since the string is used as a pattern, it might match part of another buffer name. The 'magic'-like behavior will save you in a lot of common cases, but there are still some gotchas. To avoid them, instead of calling: let buffername = bufname(a:filename) You can call: let buffername = bufname("^" . a:filename . "$")

i.e., a regex that says the entire buffer must match the filename.

keflavich commented 11 years ago

Good point, however :ls may return the full path (/path/to/file.txt), which would break the regex you suggest. let buffername = bufname(a:filename . '$') would solve most cases, but perhaps not all. I'll look into filename parsing a little more...

CptStubing commented 11 years ago

Initially, I had wrongly assumed that bufname() would always return an absolute path with the filename, but both :ls and bufname("file.txt") seem to either show you an absolute path or a relative path based on whether or not the value of pwd matches the path to the file. So, you might want to either break the name returned by bufname() down to just a filename and then prepend the path yourself, or store the value of pwd, chdir to /, call bufname(), then chdir back to the user's original pwd.... Whoo, that was a mouthful. Anyway, good luck in your endeavors.

keflavich commented 11 years ago

OK, I think I have a solution using a different regex:

let buffername = bufname('\(^\|\/\)' . a:filename . '$')

which ensures that either it's the whole pathname (i.e., /path/to/thing.txt) or just the filename (thing.txt).

The bufname approach is also good, though. Hm.

CptStubing commented 11 years ago

I think the bufname is actually not even necessary for this. I was having issues with bufnr before, and thought that it wasn't working if I passed it an absolute path and the buffer name (as displayed by bufname) was relative. I didn't realize that my windows file names in double quotes were actually getting interpreted, so "c:\foo" becomes 'c:^Lfoo'. I really need to investigate some of the vimscript debugging solutions out there.

Can you have skim send the filename with an absolute path to your bash script? Then bufno should find the correct buffer number regardless of what the pwd is at the time on the mvim server. So, all you need is this:

function! WhichTabAbs(filenameAbs)
    if (a:filenameAbs == "")
        return 0
    endif
    let bufNo = bufnr("^" . a:filenameAbs . "$")
    if (bufNo < 1)
      return 0
    endif
    for tabNo in range(1, tabpagenr("$"))
        for bufInTab in tabpagebuflist(tabNo)
            if (bufInTab == bufNo)
                return tabNo
            endif
        endfor
    endfor
    return 0
endfunction
keflavich commented 11 years ago

I'm not sure if Skim can send the full path... it's been a long time since I looked, but I think last time I did the answer was "no" and that was why I had to make WhichTab minimally smart.

Does Skim.app exist for windows? I thought not - which is why you don't have to worry about windows-type paths.

Also, in what cases does the solution I posted - let buffername = bufname('\(^\|\/\)' . a:filename . '$') - still fail? Since it's just doing a regexp search, it doesn't matter what the current path is - if the file is in the cwd, it will match ^filename$, or if it's not, it will match /filename$. Are there any other cases?

CptStubing commented 11 years ago

Yep, no Skim.app on windows. I've been using vim forever, but never bothered to learn vim script until now, so the string interpolation thing with double quotes was just something I didn't realize was happening with my paths on windows while I was testing various things, that's all.

I think you are good with that regex. Even if filename is a relative path, it will still match properly as ^filename$. The only thing I could think that might throw it off is if Skim.app is using a relative path that's relative to a different pwd than vim's, but you'd have to check and see if Skim.app even sends paths or relative paths at all.

I just wanted to point out that the call to bufname and subsequent call to bufnr is not necessary. They both do the same pattern match on the expression that's passed to them, and the rest of the logic is depending on the buffer number, so you really need to just call bufnr. The check that the parameter to the function is not an empty string is because bufnr("") tells you the number of the current buffer.

function! WhichTab(filename)
    if (a:filename == "")
        return 0
    endif
    let bufNo = bufnr('\v(^|/)' . a:filename . "$")  " see :he magic for \v
    if (bufNo < 1)
      return 0
    endif
    for tabNo in range(1, tabpagenr("$"))
        for bufInTab in tabpagebuflist(tabNo)
            if (bufInTab == bufNo)
                return tabNo
            endif
        endfor
    endfor
    return 0
endfunction
keflavich commented 11 years ago

Ah, got it, that's quite true. As it stands, if a:filename is a blank string or if the buffer isn't open in any tab, 0 is returned anyway, so I didn't even bother with the extra checks.