Hi everybody!

Attached find a new version of the patch.
Please test. Read the updated manuals.

Feel free to provide corrections and translations!

cu,
 Knut
>From 0c05e1385ea9658b32b16aafbedfa10a1e7a041e Mon Sep 17 00:00:00 2001
From: Knut Petersen <knut_peter...@t-online.de>
Date: Thu, 22 Dec 2016 01:09:07 +0100
Subject: [PATCH] Add automatic lyric extender generation.

English documentation updated.

TODO: Translations

Signed-off-by: Knut Petersen <knut_peter...@t-online.de>
---
 Documentation/learning/common-notation.itely |  13 +-
 Documentation/notation/vocal.itely           | 194 +++++++++++----------------
 lily/extender-engraver.cc                    |   6 -
 lily/lyric-extender.cc                       |  53 ++++----
 ly/music-functions-init.ly                   |  54 +++++++-
 scm/define-grob-properties.scm               |  12 ++
 scm/define-grobs.scm                         |   5 +-
 scm/music-functions.scm                      |  12 +-
 8 files changed, 197 insertions(+), 152 deletions(-)

diff --git a/Documentation/learning/common-notation.itely b/Documentation/learning/common-notation.itely
index b51766c0a2..4ca4cd8374 100644
--- a/Documentation/learning/common-notation.itely
+++ b/Documentation/learning/common-notation.itely
@@ -1136,8 +1136,9 @@ that should be included in the melisma:
 
 If a syllable extends over several notes or a single very long
 note an @notation{extender line} is usually drawn from the
-syllable extending under all the notes for that syllable.  It is
-entered as two underscores @code{__}.  Here is an example from the
+syllable extending under all the notes for that syllable.
+Lilypond adds extender lines automatically at appropriate places.
+Here is an example from the
 first three bars of @notation{Dido's Lament}, from Purcell's
 @notation{Dido and Æneas}:
 
@@ -1151,7 +1152,7 @@ first three bars of @notation{Dido's Lament}, from Purcell's
   }
   \addlyrics {
     When I am laid,
-    am laid __ in earth,
+    am laid in earth,
   }
 >>
 @end lilypond
@@ -1175,8 +1176,8 @@ far about aligning lyrics to notes.
     d4 d e | c2
   }
   \addlyrics {
-    A -- way in a __ man -- ger,
-    no __ crib for a bed,
+    A -- way in a man -- ger,
+    no crib for a bed,
   }
 >>
 @end lilypond
@@ -1230,7 +1231,7 @@ example from Handel's @notation{Judas Maccabæus}:
     c''8 | c8([ bes]) a a([ g]) f | f'4. b, | c4.~ 4
   }
   \addlyrics {
-    Let flee -- cy flocks the hills a -- dorn, __
+    Let flee -- cy flocks the hills a -- dorn,
   }
   \relative {
     \key f \major
diff --git a/Documentation/notation/vocal.itely b/Documentation/notation/vocal.itely
index 118a58e482..9d5224a4da 100644
--- a/Documentation/notation/vocal.itely
+++ b/Documentation/notation/vocal.itely
@@ -261,7 +261,7 @@ For more details, see @ref{Automatic syllable durations}.
 
 % takes durations and alignment from notes in "one"
   \new Lyrics \lyricsto "one" {
-    Life is __ _ love, live __ life.
+    Life is _ love, live life.
   }
 
 % takes durations and alignment from notes in "one" initially
@@ -636,8 +636,8 @@ hyphenated line.  This is indicated by placing a double hyphen,
 
 Alternatively, when a melisma occurs on the last or only syllable in
 a word an extender line is usually drawn from the end of the syllable
-to the last note of the melisma.  This is indicated by placing a
-double underscore, @code{__}, immediately after the word.
+to the last note of the melisma. Lilypond detects places where extender
+lines should be used and adds them automatically.
 
 There are five ways in which melismata can be indicated:
 
@@ -656,7 +656,7 @@ together:
     8
   }
   \new Lyrics \lyricsto "melody" {
-    Ky -- ri -- e __
+    Ky -- ri -- e
   }
 >>
 @end lilypond
@@ -674,7 +674,7 @@ entering lyrics:
     e8 ( d e2 )
   }
   \new Lyrics \lyricsto "melody" {
-    Ky -- ri -- e __
+    Ky -- ri -- e
   }
 >>
 @end lilypond
@@ -736,7 +736,7 @@ to be added to the melisma.
     e8 d e2
   }
   \new Lyrics \lyricsto "melody" {
-    Ky -- ri -- _ _ _ e __ _ _
+    Ky -- ri -- _ _ _ e _ _
   }
 >>
 @end lilypond
@@ -784,7 +784,7 @@ should be included in the melisma:
     g8 [ f ] ~ 4 ~ f
   }
   \new Lyrics \lyricsto "melody" {
-    Ky -- ri -- _ e __ _ _ _
+    Ky -- ri -- _ e _ _ _
   }
 >>
 @end lilypond
@@ -813,32 +813,9 @@ Notation Reference:
 Internals Reference:
 @rinternals{Tunable context properties}.
 
-@knownissues
-Extender lines under melismata are not created automatically; they
-must be inserted manually with a double underscore.
-
-
 @node Extenders and hyphens
 @unnumberedsubsubsec Extenders and hyphens
 
-@cindex melisma
-@cindex extender
-
-@c TODO cf Multiple notes to one syllable; should this be merged in?
-
-@c leave this as samp. -gp
-In the last syllable of a word, melismata are sometimes indicated with
-a long horizontal line starting in the melisma syllable, and ending in
-the next one.  Such a line is called an extender line, and it is
-entered as @samp{ __ } (note the spaces before and after the two
-underscore characters).
-
-@warning{Melismata are indicated in the score with extender lines,
-which are entered as one double underscore; but short melismata can
-also be entered by skipping individual notes, which are entered as
-single underscore characters; these do not make an extender line to be
-typeset by default.}
-
 @cindex hyphens
 
 @c leave this as samp. -gp
@@ -854,6 +831,42 @@ distance between two syllables) and the @code{minimum-length}
 (threshold below which hyphens are removed) properties of
 @code{LyricHyphen}.
 
+@cindex melisma
+@cindex extender
+
+In the last syllable of a word, melismata are normally indicated with
+a long horizontal line starting in the melisma syllable, and ending in
+the next one.  Such a line is called an extender line, and it is
+added automatically.
+
+Sometimes it is necessary to skip some notes. You can use @samp{ "" } and
+@samp{ \markup\null } in these cases.
+
+@predefined
+
+@code{\autoExtendersOn} instructs lilypond to generate lyric extenders automatically.
+
+@code{\autoExtendersOff} instructs lilypond not to generate lyric extenders
+automatically.
+
+@code{\autoExtenderLimit #num} sets the lower length limit for lyric extenders
+to num staffspaces. No lyric extenders shorter than this will be automatically
+generated. If currently inhibited, automatic generation of lyric extenders
+is also enabled.
+
+@code{\earlyExtender #num} generates a lyric extender that starts num staffspaces left
+from the associated note instead of a lyrics syllable.
+
+@code{\forceExtender} forces a lyric extenders where none would be generated otherwise.
+
+@code{\forceExtenderTo #num} forces a lyric extenders of length num staffspaces where
+none would be generated otherwise.
+
+@code{\noExtender} suppresses a lyric extender that would be automatically generated.
+
+@code{\shortenExtender #num} tells lilypond to stop the next lyric extender num
+staffspaces left from the normal endpoint.
+
 @seealso
 Internals Reference:
 @rinternals{LyricExtender},
@@ -1284,24 +1297,18 @@ More verses may be added in a similar way:
     \new Staff {
       \new Voice = "singleVoice" {
         \relative {
-	  a'4 a a a
-	  \repeat volta 3 { b4 b b b }
+          a'4 a a a
+          \repeat volta 3 { b4 b b b }
           c4 c c c
-	}
+        }
       }
     }
     \new Lyrics \lyricsto "singleVoice" {
       Not re -- peat -- ed.
       <<
         { The first time words.	}
-	\new Lyrics {
-	  \set associatedVoice = "singleVoice"
-	  Sec -- ond time words.
-	}
-	\new Lyrics {
-	  \set associatedVoice = "singleVoice"
-	  The third time words.
-	}
+        \new Lyrics { Sec -- ond time  words. }
+        \new Lyrics { The third time words. }
       >>
       The end sec -- tion.
     }
@@ -1324,26 +1331,22 @@ To position them correctly use @code{alignBelowContext}:
     \new Staff {
       \new Voice = "melody" {
         \relative {
-	  a'4 a a a
-	  \repeat volta 3 { b4 b b b }
+          a'4 a a a
+          \repeat volta 3 { b4 b b b }
           c4 c c c
-	}
+        }
       }
     }
     \new Lyrics = "firstVerse" \lyricsto "melody" {
       Not re -- peat -- ed.
       <<
         { The first time words.	}
-	\new Lyrics = "secondVerse"
-        \with { alignBelowContext = #"firstVerse" } {
-	  \set associatedVoice = "melody"
-	  Sec -- ond time words.
-	}
-	\new Lyrics = "thirdVerse"
-        \with { alignBelowContext = #"secondVerse" } {
-	  \set associatedVoice = "melody"
-	  The third time words.
-	}
+         \new Lyrics = "secondVerse"
+            \with { alignBelowContext = #"firstVerse" }
+            { Sec -- ond time words. }
+         \new Lyrics = "thirdVerse"
+            \with { alignBelowContext = #"secondVerse" }
+            { The third time words. }
       >>
       The end sec -- tion.
     }
@@ -1396,25 +1399,18 @@ lyrics correctly.
 }
 @end lilypond
 
-@funindex \skip
 @cindex skipping notes in lyrics
 @cindex lyrics, skipping notes
 
 But when the repeated section has different words, or when one
 of the @code{\alternative} blocks starts with a rest, a repeat
-construct cannot be used around the words and @code{\skip} commands
+construct cannot be used around the words and @samp{ "" }
 have to be inserted manually to skip over the notes in the
 alternative sections which do not apply.
 
 Note: do not use an underscore, @code{_}, to skip notes -- an
 underscore indicates a melisma, causing the preceding syllable
-to be left-aligned.
-
-@warning{The @code{@bs{}skip} command must be followed by a number,
-but this number is ignored in lyrics which derive their durations
-from the notes in an associated melody through @code{\addlyrics} or
-@code{\lyricsto}.  Each @code{@bs{}skip} skips a single note of any
-value, irrespective of the value of the following number.}
+to be left-aligned and an extender to be generated.
 
 @lilypond[verbatim,quote,ragged-right]
 \score {
@@ -1431,15 +1427,12 @@ value, irrespective of the value of the following number.}
     }
     \new Lyrics {
       \lyricsto "melody" {
-        The first time words.
-        \repeat unfold 2 { \skip 1 }
-        End here.
+        The first time words. "" "" End here.
       }
     }
     \new Lyrics {
       \lyricsto "melody" {
-        Sec -- ond
-        \repeat unfold 2 { \skip 1 }
+        Sec -- ond "" ""
         time words.
       }
     }
@@ -1462,8 +1455,13 @@ The tie creates a melisma into the first alternative, but not into
 the second and subsequent alternatives, so to align the lyrics
 correctly it is necessary to disable the automatic creation of
 melismata over the volta section and insert manual skips.
+Note the use of @code{\earlyExtender}.
 
 @lilypond[quote,verbatim]
+\paper {
+  ragged-right = ##f
+}
+
 \score {
   <<
     \new Staff {
@@ -1480,10 +1478,10 @@ melismata over the volta section and insert manual skips.
     }
     \new Lyrics {
       \lyricsto "melody" {
-        \repeat volta 2 { Here's a __ }
+        \repeat volta 2 { Here's a }
         \alternative {
-          { \skip 1 verse }
-          { \skip 1 sec }
+          { _ verse }
+          { \earlyExtender #-4  sec -- }
         }
         ond one.
       }
@@ -1500,40 +1498,15 @@ When the repeated section has different words a @code{\repeat}
 cannot be used around the lyrics and @code{\skip} commands need to
 be inserted manually, as before.
 
+@code{\shortenExtender} and @code{\earlyExtender} can be used together
+with skips coded as @code{""} to give an optimal solution to the problem
+of lyric extenders in such situations:
+
 @lilypond[quote,verbatim]
-\score {
-  <<
-    \new Staff {
-      \time 2/4
-      \new Voice = "melody" {
-        \relative {
-          \repeat volta 2 { b'4 b ~}
-          \alternative { { b b } { b \repeatTie c } }
-          c4 c
-        }
-      }
-    }
-    \new Lyrics {
-      \lyricsto "melody" {
-        Here's a __ verse.
-        \repeat unfold 2 { \skip 1 }
-      }
-    }
-    \new Lyrics {
-      \lyricsto "melody" {
-        Here's one
-        \repeat unfold 2 { \skip 1 }
-        more to sing.
-      }
-    }
-  >>
+\paper {
+  ragged-right = ##f
 }
-@end lilypond
 
-If you wish to show extenders and hyphens into and out of
-alternative sections these must be inserted manually.
-
-@lilypond[quote,verbatim]
 \score {
   <<
     \new Staff {
@@ -1548,15 +1521,12 @@ alternative sections these must be inserted manually.
     }
     \new Lyrics {
       \lyricsto "melody" {
-        Here's a __ verse.
-        \repeat unfold 2 { \skip 1 }
+        Here's a verse. "" ""
       }
     }
     \new Lyrics {
       \lyricsto "melody" {
-        Here's "a_"
-        \skip 1
-        "_" sec -- ond one.
+          Here's \shortenExtender #4 one "" \earlyExtender #-4  more to sing.
       }
     }
   >>
@@ -1667,7 +1637,7 @@ too short, since the lyrics are aligned only to the top voice:
 @lilypond[quote,verbatim]
 soprano = \relative { b'8( c d c) d2 }
 alto = \relative { g'2 b8( a g a) }
-words = \lyricmode { la __ la __ }
+words = \lyricmode { la  la }
 
 \new Staff <<
   \new Voice = "sopranoVoice" { \voiceOne \soprano }
@@ -1686,7 +1656,7 @@ appropriately:
 soprano = \relative { b'8( c d c) d2 }
 alto = \relative { g'2 b8( a g a) }
 aligner = \relative { b'8( c d c) b( a g a) }
-words = \lyricmode { la __ la __ }
+words = \lyricmode { la la }
 
 \new Staff <<
   \new Voice { \voiceOne \soprano }
@@ -1703,7 +1673,7 @@ function, which does not allow lyrics on its own:
 soprano = \relative { b'8( c d c) d2 }
 alto = \relative { g'2 b8( a g a) }
 aligner = \relative { b'8( c d c) b( a g a) }
-words = \lyricmode { la __ la __ }
+words = \lyricmode { la la }
 
 \new Staff <<
   \new Voice \partcombine \soprano \alto
@@ -1728,7 +1698,7 @@ soprano = \relative { b'8( c d c) d2 }
 altoOne = \relative { g'2 b8( a b4) }
 altoTwo = \relative { d'2 g4( fis8 g) }
 aligner = \relative { b'8( c d c) d( d d d) }
-words = \lyricmode { la __ la __ }
+words = \lyricmode { la la }
 
 \new ChoirStaff \with {\accepts NullVoice } <<
   \new Staff \soprano
@@ -1944,7 +1914,7 @@ block:
   }
   \new Lyrics \with { includeGraceNotes = ##t }
   \lyricsto melody {
-    Ah __ fa
+    Ah fa
   }
 >>
 @end lilypond
diff --git a/lily/extender-engraver.cc b/lily/extender-engraver.cc
index 39fa7788de..c5b9587022 100644
--- a/lily/extender-engraver.cc
+++ b/lily/extender-engraver.cc
@@ -162,18 +162,12 @@ Extender_engraver::finalize ()
   if (extender_)
     {
       completize_extender (extender_);
-
-      if (!extender_->get_bound (RIGHT))
-        extender_->warning (_ ("unterminated extender"));
       extender_ = 0;
     }
 
   if (pending_extender_)
     {
       completize_extender (pending_extender_);
-
-      if (!pending_extender_->get_bound (RIGHT))
-        pending_extender_->warning (_ ("unterminated extender"));
       pending_extender_ = 0;
     }
 }
diff --git a/lily/lyric-extender.cc b/lily/lyric-extender.cc
index 8afe2c5569..c1404b6961 100644
--- a/lily/lyric-extender.cc
+++ b/lily/lyric-extender.cc
@@ -45,51 +45,54 @@ Lyric_extender::print (SCM smob)
   common = common->common_refpoint (me->get_system (), X_AXIS);
 
   Real sl = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
+  bool start_of_part_b = !left_edge->internal_has_interface (ly_symbol2scm ("lyric-syllable-interface"));
+  bool end_of_part_a = me->get_bound (RIGHT)->break_status_dir ();
+  bool force_extender = to_boolean (me->get_property ("force-extender"));
+  bool no_extender = to_boolean (me->get_property ("no-extender"));
 
-  extract_grob_set (me, "heads", heads);
+  if (!force_extender && no_extender)
+    return SCM_EOL;
 
-  if (!heads.size ())
+  extract_grob_set (me, "heads", heads);
+  if (!heads.size () || (!force_extender && !start_of_part_b && !end_of_part_a && heads.size () < 2))
     return SCM_EOL;
 
   common = common_refpoint_of_array (heads, common, X_AXIS);
 
   Real left_point = 0.0;
-  if (left_edge->internal_has_interface (ly_symbol2scm ("lyric-syllable-interface")))
+  if (!start_of_part_b)
     left_point = left_edge->extent (common, X_AXIS)[RIGHT];
-  else if (heads.size ())
-    left_point = heads[0]->extent (common, X_AXIS)[LEFT];
   else
-    left_point = left_edge->extent (common, X_AXIS)[RIGHT];
+    left_point = heads[0]->extent (common, X_AXIS)[LEFT];
 
   if (isinf (left_point))
     return SCM_EOL;
 
-  /* It seems that short extenders are even lengthened to go past the
-     note head, but haven't found a pattern in it yet. --hwn 1/1/04  */
-  SCM minlen = me->get_property ("minimum-length");
-  Real right_point
-    = left_point + (robust_scm2double (minlen, 0));
-
-  right_point = min (right_point, me->get_system ()->get_bound (RIGHT)->relative_coordinate (common, X_AXIS));
-
-  if (heads.size ())
-    right_point = max (right_point, heads.back ()->extent (common, X_AXIS)[RIGHT]);
-
   Real h = sl * robust_scm2double (me->get_property ("thickness"), 0);
   Drul_array<Real> paddings (robust_scm2double (me->get_property ("left-padding"), h),
                              robust_scm2double (me->get_property ("right-padding"), h));
+  Real force_len = robust_scm2double (me->get_property ("force-length"), 0);
+  Real collapse_len = robust_scm2double (me->get_property ("collapse-length"), 0);
+
+  Real right_point = heads.back ()->extent (common, X_AXIS)[RIGHT];
+
+  if (force_extender)
+    right_point = max (heads.back ()->extent (common, X_AXIS)[RIGHT], left_point + force_len);
 
   if (right_text)
-    right_point = min (right_point, (robust_relative_extent (right_text, common, X_AXIS)[LEFT] - paddings[RIGHT]));
+    right_point = min (right_point, (robust_relative_extent (right_text, common, X_AXIS)[LEFT]));
+
+  if (end_of_part_a)
+    right_point = robust_relative_extent (me->get_bound (RIGHT), common, X_AXIS)[LEFT];
+
+  right_point -= paddings[RIGHT];
 
-  /* run to end of line. */
-  if (me->get_bound (RIGHT)->break_status_dir ())
-    right_point = max (right_point, (robust_relative_extent (me->get_bound (RIGHT), common, X_AXIS)[LEFT] - paddings[RIGHT]));
+  if (!start_of_part_b)
+    left_point += paddings[LEFT];
 
-  left_point += paddings[LEFT];
   Real w = right_point - left_point;
 
-  if (w < 1.5 * h)
+  if (w < collapse_len && !force_extender && !end_of_part_a && !start_of_part_b)
     return SCM_EOL;
 
   Stencil mol (Lookup::round_filled_box (Box (Interval (0, w),
@@ -106,9 +109,13 @@ ADD_INTERFACE (Lyric_extender,
                " note).",
 
                /* properties */
+               "collapse-length "
+               "force-extender "
+               "force-length "
                "heads "
                "left-padding "
                "next "
+               "no-extender "
                "right-padding "
                "thickness "
               );
diff --git a/ly/music-functions-init.ly b/ly/music-functions-init.ly
index 1f94319bce..babfaed3e2 100644
--- a/ly/music-functions-init.ly
+++ b/ly/music-functions-init.ly
@@ -202,6 +202,29 @@ staves.")
       (context-spec-music (make-skip-music skip) 'Staff
                           "down" clef-2)))))
 
+autoExtenderLimit =
+#(define-music-function (num) (number?)
+   (_i "Set the lower length limit for lyric extenders
+to @var{num} staffspaces. No lyric extenders shorter than
+this will be automatically generated. If currently inhibited,
+automatic generation of lyric extenders is also enabled.")
+   #{ \autoExtendersOn
+      \override Lyrics.LyricExtender.collapse-length = #num #})
+
+autoExtendersOff =
+#(define-music-function () ()
+   (_i "Instructs lilypond not to generate lyric extenders
+automatically.")
+   #{ \override LyricExtender.no-extender = ##t
+      \override LyricExtender.force-extender = ##f #})
+
+autoExtendersOn =
+#(define-music-function () ()
+   (_i "Instructs lilypond to generate lyric extenders
+automatically.")
+   #{ \override LyricExtender.no-extender = ##f
+      \override LyricExtender.force-extender = ##f #})
+
 balloonGrobText =
 #(define-music-function (grob-name offset text)
    (symbol? number-pair? markup?)
@@ -394,7 +417,13 @@ displayScheme =
      (display-scheme-music expr port))
    expr)
 
-
+earlyExtender =
+#(define-music-function (num) (number?)
+  (_i "Generate a lyric extender that starts @var{num}
+staffspaces left from the associated note instead of
+a lyrics syllable.")
+   #{ \forceExtender
+    \lyricmode { \tweak self-alignment-X #num \markup\null } #})
 
 endSpanners =
 #(define-music-function (music) (ly:music?)
@@ -495,6 +524,17 @@ to the preceding note or rest as a post-event with @code{-}.")
                'footnote-text footnote)))
      (once (propertyTweak 'footnote-music mus item))))
 
+forceExtender =
+#(define-music-function () ()
+   (_i "Force a lyric extender once.")
+   #{ \once \override LyricExtender.force-extender = ##t #})
+
+forceExtenderTo =
+#(define-music-function (num) (number?)
+   (_i "Force a lyric extender of length @var{num} staffspaces.")
+   #{ \forceExtender
+      \once \override Lyrics.LyricExtender.force-length = #num #})
+
 grace =
 #(def-grace-function startGraceMusic stopGraceMusic
    (_i "Insert @var{music} as grace notes."))
@@ -879,6 +919,12 @@ musicMap =
    (_i "Apply @var{proc} to @var{mus} and all of the music it contains.")
    (music-map proc mus))
 
+noExtender =
+#(define-music-function () ()
+   (_i "Suppress a lyric extender that would be automatically
+generated.")
+   #{ \once \override LyricExtender.no-extender = ##t #})
+
 %% noPageBreak and noPageTurn are music functions (not music indentifiers),
 %% because music identifiers are not allowed at top-level.
 noPageBreak =
@@ -1680,6 +1726,12 @@ shiftDurations =
 
    (shift-duration-log arg dur dots))
 
+shortenExtender =
+#(define-music-function (num) (number?)
+   (_i "Stop the next lyric extender @var{num} staffspaces
+left from the normal endpoint.")
+   #{ \once \override LyricExtender.right-padding = #num #})
+
 single =
 #(define-music-function (overrides music)
    (ly:music? ly:music?)
diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm
index c234792dd2..41b6139313 100644
--- a/scm/define-grob-properties.scm
+++ b/scm/define-grob-properties.scm
@@ -185,6 +185,9 @@ that should be used for clef modifiers with various clefs")
 edges of beams?")
      (collapse-height ,ly:dimension? "Minimum height of system start
 delimiter.  If equal or smaller, the bracket/@/brace/@/line is removed.")
+     (collapse-length ,ly:dimension? "An automatically generated
+lyric extender is suppressed if it would be shorter than
+this length.")
      (collision-interfaces ,list? "A list of interfaces for which
 automatic beam-collision resolution is run.")
      (collision-voice-only ,boolean? "Does automatic beam collsion apply
@@ -330,9 +333,16 @@ allowed.")
      (footnote ,boolean? "Should this be a footnote or in-note?")
      (footnote-music ,ly:music? "Music creating a footnote.")
      (footnote-text ,markup? "A footnote for the grob.")
+     (force-extender ,boolean? "Force a lyric extender to be generated
+if none would be generated otherwise and/or force it to be at least as
+wide as indicated by property @code{length-limit-or-forced-length} unless
+it would collide with the next syllable. Overrides property
+@code{no-extender}.")
      (force-hshift ,number? "This specifies a manual shift for notes
 in collisions.  The unit is the note head width of the first voice
 note.  This is used by @rinternals{note-collision-interface}.")
+     (force-length ,ly:dimension? "A forced lyric extender
+is given this length if possible.")
      (forced-spacing ,number? "Spacing forced between grobs, used in
 various ligature engravers.")
      (fraction ,fraction? "Numerator and denominator of a time
@@ -663,6 +673,8 @@ syllable following an extender).")
      (no-alignment ,boolean? "If set, don't place this grob in a
 @code{VerticalAlignment}; rather, place it using its own
 @code{Y-offset} callback.")
+     (no-extender ,boolean? "Inhibits generation of lyric extenders.
+Can be overridden by property @code{force-extender}.")
      (no-ledgers ,boolean? "If set, don't draw ledger lines on this
 object.")
      (no-stem-extend ,boolean? "If set, notes with ledger lines do not
diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm
index e36ea12bf2..714aff5b1a 100644
--- a/scm/define-grobs.scm
+++ b/scm/define-grobs.scm
@@ -1361,7 +1361,10 @@
 
     (LyricExtender
      . (
-        (minimum-length . 1.5)
+        (collapse-length . 1.5)
+        (force-extender . #f)
+        (force-length . 1.5)
+        (no-extender . #f)
         (stencil . ,ly:lyric-extender::print)
         (thickness . 0.8) ; line-thickness
         (Y-extent . (0 . 0))
diff --git a/scm/music-functions.scm b/scm/music-functions.scm
index b1dc2f9c61..8cb9cf7ac5 100644
--- a/scm/music-functions.scm
+++ b/scm/music-functions.scm
@@ -728,10 +728,16 @@ making it possible to @code{\\revert} to any previous value afterwards."
          'articulation-type name
          properties))
 
+(define (add-extender! event)
+   (ly:music-set-property! event 'articulations
+     (append (ly:music-property event 'articulations)
+             (list (make-music (quote ExtenderEvent)))))
+   event)
+
 (define-public (make-lyric-event string duration)
-  (make-music 'LyricEvent
-              'duration duration
-              'text string))
+  (if (and (string? string)(string=? " " string))
+      (make-music 'LyricEvent 'duration duration 'text string)
+      (add-extender! (make-music 'LyricEvent 'duration duration 'text string))))
 
 (define-safe-public (make-span-event type span-dir)
   (make-music type
-- 
2.11.0

_______________________________________________
lilypond-devel mailing list
lilypond-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/lilypond-devel

Reply via email to