Re: An interesting little poser [PS]

2020-06-30 Thread Tim Chase
On 2020-06-30 18:33, Chris Jones wrote:
> On Mon, Jun 29, 2020 at 03:34:03PM EDT, Tim Chase wrote:
>> Try changing that line from
>> 
>>   $?^$?+,$g/^\[\@!.*\n\[\@!/,/\n\[\|\%$/j
>> 
>> to
>> 
>>   $?^$?+,$g/^\[\@!.*\n\[\@!./,/\n\[\|\%$/j  
> 
> This indeed fixes the problem. 

Great!

> Still trying to wrap my head around this bizarre 
> 
>   :g/regex1/,/regex2/command

It helps to think of broken down.  The :g command itself can take one
range:

  :{range1}g/regex1/{command}

and that {command} can consist of a command *and an optional range
relative to each of those matches*.  So in the above, that {command}
is

  {range2}command

and that {range2} is

  ,/regex2/

which has the start of the range "." implied, so would be the same as

  .,/regex2/

The power of this "find lines with a :g command, then operate on a
range of lines relative to each of those matches"


So it might be clearer to write that original as

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

   222 3 4

where

1 = the range for the footnotes
2 = find lines where the next line is a continuation
3 = a range from this continued-line through the end of the footnote
4 = join that range of lines from #3

To learn this requires pulling together bits from several sections of
the help:

  :help range
  :help search-offset
  :help 10.3  " particularly the ADD AND SUBTRACT section
  :help 10.4

so it's certainly not a well-advertised bit of functionality.

But as you can see, it's an amazingly *powerful* bit of functionality
that I use all the time.

> Thank you very much for your help!

Thanks for the fun challenge. :-)

-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+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_use/20200630184130.444ac212%40bigbox.attlocal.net.


Re: An interesting little poser [PS]

2020-06-30 Thread Chris Jones
On Mon, Jun 29, 2020 at 03:34:03PM EDT, Tim Chase wrote:

> Try changing that line from
> 
>   $?^$?+,$g/^\[\@!.*\n\[\@!/,/\n\[\|\%$/j
> 
> to
> 
>   $?^$?+,$g/^\[\@!.*\n\[\@!./,/\n\[\|\%$/j

This indeed fixes the problem. 

Still trying to wrap my head around this bizarre 

  :g/regex1/,/regex2/command ... syntax.

I eventually found something in the user manual that (sort of) explains
what's going on here... under [edit-paragraph-join].

What I understand is that somewhat in a 'shorthand manner'... with the
:g(lobal) command you can specify a second /patttern2/ (separated from
the 'normal' g /pattern1/ by a comma) defining a range for the command
executed by :global... or in other words that the command executed by
':g' will target not the current line (as is normally the case) but
rather all the lines in the range from 'current line' (the line :g is
currently point at) to the next line in the buffer that matches
/pattern2/...

Rather comical that I spent so much time wondering what that comma
somewhere in the middle of what I assumed was one single regex was
about...! never imagined this comma in '/pattern1/,/pattern2/' was not
part of a regex but rather the range separator... Incidentally this
clarifies vim's 'invalid range' message - i.e. it's the join command's
range he's complaining about... not the global :g range!

In any case the modified Unfootnote() function now does the job without
a glitch.

Thank you very much for your help!

CJ

-- 
-- 
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+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_use/20200630223309.GD4819%40turki.local.


Re: An interesting little poser [PS]

2020-06-29 Thread Tim Chase
[sorry for the belated reply.]

On 2020-06-27 22:46, Chris Jones wrote:
> Not sure if it's some kind of corner case or this little
> experimented has unearthed a bug in Vim's regex logic... 

Not a bug in the regex parser.  See further diagnosis:

> On Mon, Jun 22, 2020 at 09:40:38PM EDT, Tim Chase wrote:
> > " make all the footnotes on one line
> > $?^$?+,$g/^\[\@!.*\n\[\@!/,/\n\[\|\%$/j

The problem lies here.  If you remove the second half of the range

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

vim is finding lines that need to be joined.  However in your example

> The problem occurs for instance with the following example file:
> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[snip]
> [36]
> Ullam consequatur.
> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

that last line ends in a newline that isn't followed by a \[ so my
regex thinks it should join this.  But then when it goes to find the
ending of the range to join, it looks for something slightly
different.

Try changing that line from

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

to

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

(adding that "." after the "make sure a literal [ doesn't happen
here") to ensure that there's something for the line to join.

Seems to resolve the issue for me without breaking my other
test-cases that I threw at it.

You might still get "errors" (more like complaints) if you have a
file containing 0 multi-line footnotes, telling you "Pattern not
found". But that should be ignorable because there simply weren't any
footnote lines for it to join together.

-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+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_use/20200629143403.1fc5d1f7%40bigbox.attlocal.net.


Re: An interesting little poser [PS]

2020-06-28 Thread Chris Jones
On Mon, Jun 22, 2020 at 09:40:38PM EDT, Tim Chase wrote:
> 
>   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

When I run the above function (as copy-pasted from this thread) I get an
'E16: Invalid range' error on the g(global) command on some files.
Interestingly the function still does the job as if nothing had
happened. I went back to the explanation you gave in your earlier post
and after comparing with other files where I do not get the error
I can't find what's wrong with it. It looks like the range defining the
footnotes section is perfectly valid and that the Vim has
a problem with the pattern used in the g:/pattern/j command (I did
a $?^$?+,$print and it prints the footnotes section to the screen and
does not yell at me).

The interesting point is that the g: command's pattern has two parts
separated by a comma... which makes it suspiciously look like...
a range!

So is Vim confused by the regex that makes up the /pattern/…?

The problem occurs for instance with the following example file:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
Debitis neque ipsum eos aut. Voluptatum ratione voluptatum facere voluptas.
Maxime qui ad autem ducimus quae [36]. Officia labore iusto voluptas ad quia
nostrum.


[36]
Ullam consequatur.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

But if I add a carriage return between Ullam & consequatur like so:

[36]
Ullam 
consequatur.

... the problem goes away!

Not sure if it's some kind of corner case or this little experimented
has unearthed a bug in Vim's regex logic... and unfortunately I have no
idea how to investigate further.

Assuming you manage to reproduce the error is there any way you could
take a (quick) look?

Thanks,

CJ

-- 
-- 
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+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_use/20200628024608.GB4819%40turki.local.


Re: An interesting little poser [PS]

2020-06-26 Thread Chris Jones
On Mon, Jun 22, 2020 at 09:40:38PM EDT, Tim Chase wrote:

Sorry for the delay... needed time for this to sink in (and test)

> 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

Works just fine! I wrapped the function in an if/endif block to make
sure there does exist footnotes in any given file (some of the files do
not have a footnotes section at the bottom)

  :normal gg
  :let b:s=getpos('.')
  " check for the existence of a footnotes
  :silent! normal /\[\d\+]
  :let b:e=getpos('.')
  if b:s[1] != b:e[1]

Testing whether the search was successful which would cause the cursor
to move... Couldn't figure out a nicer way...

> 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. :-)

That at least hadn't escaped me... so much so that I wasted a couple of
hours trying to figure out why a ':normal /pattern' worked at the prompt
and didn't work when I coded it in a function... the cursor just refused
to budge and no matter how much I tested I couldn't figure out why...
until I calmed down and realized that what I coded was NOT what I had
actually typed at the ex prompt:

  :normal /pattern
 # ex command PLUS  - big difference!

...

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

It is perfectly suitable thank you! Without the if/endif block it didn't
break anything... just joined all the lines in the last paragraph of the
file (which didn't matter since I later reflow everything anyway — see
the other function I came up with).

Here's the function I wrote (I could integrate the code to the one you
kindly provided to do it all in one pass):

  function! Delfoot()
:normal gg
:let b:s=getpos('.')
:silent! normal /^$\n\[[0-9]\{1,}]
:let b:e=getpos('.')
if b:s[1] != b:e[1]
  :let b:f=expand('%:t') 
  :let b:fn='../ftns/ft'.b:f[2:5].'.txt'
  :execute ".,$w " b:fn
  :execute ".,$d"
endif
:set tw=78
:normal ggVGgq
  endfunc   

Nothing clever about this one! :-) ... I backup the foonote section at
the end of each file before I do away with it (and reflow everything to
a sensible textwidth to finish off the job so the files are ready for
edit/proofreading).

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

Made up for that depressing feeling I get whenever I venture into vim
script coding and realize I have forgotten the little I know.

I eventually found a vim fandom article that has some examples of what
can be done with syntaxically built ranges 

Thank,

CJ

-- 
-- 
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+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_use/20200626204633.GA4819%40turki.local.


Re: An interesting little poser [PS]

2020-06-22 Thread Tim Chase
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 

Re: An interesting little poser [PS]

2020-06-22 Thread Chris Jones
Congrats! Works out of the box and does exactly what I had in mind:
copy-pasted the commands and the result is spectacular. The clever idea
is to use the :g(lobal) command to build the array.

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

Having done this I'm back to where I was (but with one-line notes)
& your solution will work the same.

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 — and I'm damned if I can figure out how to
read this particular bit of magic...

Since I couldn't think of a way to highlight the targeted section of the
file I did a: 

  :$?^$?,$w /tmp/t.txt 

... and I saw that the footnotes section at the end of the files was
excluded from the resulting /tmp/t.txt file.

Please explain.

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?

Well... I had a hunch this could be 'done the smart way' simply using
basic vim commands... gladd I asked...

Thanks,

CJ

> > To build this associative array, you might try
> > 
> > :$?^$?,$g/\[\d\+\]/let b:a[getline('.')]=getline(line(".")+1)
> 
> Whoops, before you do this, you might have to let vim know that b:a
> is an array:
> 
>   :let b:a={}
>   :$?^$?,$g/\[\d\+\]/let b:a[getline('.')]=getline(line(".")+1)
> 
> > You can then use
> > 
> >  :1,$?^$?s/\[\d\+]/\='^['.b:a[submatch(0)].']'/g
> 
> Oh, this also assumes that all footnote-references have corresponding
> entries in the footnote block.  If you have a [13] and there's no
> [13] footnote at the bottom, that substitute will yell at you about a
> "E716: Key not present in Dictionary: {bogus footnote}"

-- 
-- 
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+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_use/20200623001643.GB25135%40turki.local.


Re: An interesting little poser [PS]

2020-06-21 Thread Tim Chase
On 2020-06-21 18:57, Tim Chase wrote:
> To build this associative array, you might try
> 
> :$?^$?,$g/\[\d\+\]/let b:a[getline('.')]=getline(line(".")+1)

Whoops, before you do this, you might have to let vim know that b:a
is an array:

  :let b:a={}
  :$?^$?,$g/\[\d\+\]/let b:a[getline('.')]=getline(line(".")+1)

> You can then use
> 
>  :1,$?^$?s/\[\d\+]/\='^['.b:a[submatch(0)].']'/g

Oh, this also assumes that all footnote-references have corresponding
entries in the footnote block.  If you have a [13] and there's no
[13] footnote at the bottom, that substitute will yell at you about a
"E716: Key not present in Dictionary: {bogus footnote}"

-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+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_use/20200621190214.7ca5c9ea%40bigbox.attlocal.net.