Monday, June 22, 2020

Re: An interesting little poser [PS]

On 2020-06-22 20:16, Chris Jones wrote:
> Congrats! Works out of the box and does exactly what I had in mind

Great!

> copy-pasted the commands and the result is spectacular. The clever
> idea is to use the :g(lobal) command to build the array.

I do love the power of the :g command and abuse it regularly

> I posted a little too fast and left out an important third³
> footnote at the bottom of my message... something like 'the
> footnotes in the example are only one line long so as not to
> clutter up your screen... but in my use case many are multi-line'.
>
> But after playing with my files for a while it's pretty easy to
> reflow the footnotes so that they're only one line long:
>
> 1. add a null line between the footnotes
> 2. reflow them with a largee textwidth value
> 3. add a line break after each [nnn] in column 1
> 4. remove the null lines added in 1

Glad you were able to hack a solution. Again abusing the :g command,
I might have done the preprocessing as

:$?^$?+,$g/^\[\@!.*\n\[\@!/,/\n\[\|\%$/j

to do the joining for me :-) That uses the range for the footnotes
(see below) and looks for any line that doesn't start with a "["
character ("\[\@!"), followed by whatever on that line (".*"),
followed by a newline ("\n"), followed by the assertion that a
literal "[" doesn't start on this 2nd line either ("\[\@!"). Or
roughly translated "find footnote text that is continued on the next
line". Starting at that point, create a second range from there (that
second ",") that goes through either the newline followed by a
literal "[" (another footnote follows here) or ("\|") the end of file
("\@$") and join those lines together ("j").

It's big & ugly, but it does the job in one go.

> What are all these magical dollar signs in the "g(lobal)" and
> "s(ubstitute)" commands? They appear to define a vim command
> 'range' but since I've never used ranges

A range can be modified relative to each location. So the range of
lines footnote lines we're interested in are

:$?^$?,$

Breaking that down, the first "$" says "starting at the last line of
the file", then ?…? search backwards until we find "^$" (an empty
line), and start the range there. In the above :j(oin) example, I
start the range one line after that ("+" which is the same as "+1").
The comma delineates the start of the range (that line we just found,
roughly defined as either "the blank line preceeding the last line of
the file" or with the "+" it's "the line after the blank line
preceeding the last line of the file"). So now after the comma we
define the end of the range as the last line of the file ("$").

For the body-text of your document, the range is "1" (the first line
in the file) through (",") the "blank line preceeding the last line
in the file" (same as before, "$?^$?").

> Since I couldn't think of a way to highlight the targeted section
> of the file I did a:
>
> :$?^$?,$w /tmp/t.txt

For any of these ranges, you could also use other ways of defining
them. If the last blank line in the file is line 314, then the
footnotes are

:315,$

and the body text would be the range

:1,313

Alternatively you could highlight them in visual mode with the "V"
command and vim would automatically populate the range as

:'<,'>

(i.e. "from the first visually-selected line through the last
visually-selected line").

The advantage to describing the range syntactically is that it
doesn't need to manually find those line-numbers or manually select
things visually, allowing you to automate it across multiple files as
is your next request: ;-)

> Oh, and how would I go about running this sequence of commands on
> a bunch of open file (buffers) in a vim session? what would the best
> tactics to do :bufdo of all these commands?

In this case because each of the commands involves the :g command, it
becomes a bit trickier. I'd likely define a function something like

function! Unfootnote()
" make all the footnotes on one line
$?^$?+,$g/^\[\@!.*\n\[\@!/,/\n\[\|\%$/j
" gather the footnotes into b:a
let b:a={}
$?^$?,$g/\[\d\+\]/let b:a[getline('.')]=getline(line(".")+1)
" find all the footnote references
" and replace them with the corresponding text
1,$?^$?s/\[\d\+]/\='^['.b:a[submatch(0)].']'/g
endfunc

Note how each of those Ex commands we've discussed is a valid command
in the body of a function as well. Yet another obscure corner of vim
that escapes many folks. :-)

You should then be able to invoke that across all the files with
bufdo/argdo, something like

:set hidden
:bufdo call Unfootnote()

and, after reviewing that it did what you wanted, issue

:wall

if they meet your needs.

Note that, while the parts of the function are reasonably tested, this
function itself is largely untested, but *should* be pretty close.

Hopefully this both makes sense and helps level up your vim, letting
you get drunk on this new-found power. :-)

-tim



--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vim_use+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/vim_use/20200622204038.72a9ac0a%40bigbox.attlocal.net.

No comments: