samcarter
I'm looking for a way to include (only) the comments from a .sty file as actual text in another document.

At first I thought about using the catchfilebetweentags package, but this shallows line endings and can't deal with verbatim content.

Are there any other ways to include commented lines from another file?

MWE (I'm not set on any specific syntax, feel free to add tags/characters/whatever as long as the lines stay commented in the .sty file)


\documentclass{article}

\begin{filecontents*}[overwrite]{duck.sty}

\newcommand{\quack}{Quack!}

% line to be included in the other document
% \begin{verbatim}
% something verbatim to be included in other document
% \end{verbatim}

%% line not to be included

\end{filecontents*}

\begin{document}

some text

some magic is needed here to include the line "line to be included in the other document" and the verbatim material here

\end{document}


*Background: I like the concept of keeping the documentation close to the source, like it is done in .dtx files, however I would prefer to directly have the .sty file available (this makes it easier to switch between different versions/branches with git and is also easier for users to install if they don't have deep latex knowledge).*

Phelype Oleinik
Here's one possibility.  The command \CommentInput{⟨file⟩} reads the ⟨file⟩, parsing it line by line, and rewriting the lines that start with a single % to a file \jobname.cif (cif stands for Comment Input File) without the leading %, then proceeds to read the \jobname.cif file back in.

I don't see how it would be possible to do this without the intermediate file while keeping the possibility of verbatim environments and commands with arguments spanning more than one line.  But I think the extra file shouldn't be that big of an issue.

The code takes care to make a file substitution so that file hooks still work by name on the original file you want to read in, rather than on the temporary file.

This behaves different from \DocInput regarding the % sign:  \DocInput makes a % catcode-9, so it's completely ignored (anywhere in the line) while here only the first % is removed and the other ones are still comment characters (also ^^A is not a comment char then).  This is harder to change because then unless the doc package is loaded, the verbatim environment will see the should-be-ignored start-of-line % character and will typeset it.  It is easy to change though, if you intend to use this with doc and prefer the all-%-are-ignored behaviour.

And without further ado, here's the code:

\documentclass{article}

\begin{filecontents*}[overwrite]{duck.sty}

\newcommand{\quack}{Quack!}

% line to be included in the other document
% \begin{verbatim}
% something verbatim to be included in other document
% \end{verbatim}

%% line not to be included

\end{filecontents*}

\ExplSyntaxOn
\makeatletter
\iow_new:N \l__samcarter_iow
\tl_new:N \l__samcarter_file_name_tl
\NewDocumentCommand \CommentInput { m }
{ \samcarter_comment_input:n {#1} }
\cs_new_protected:Npn \samcarter_comment_input:n #1
{
\file_get_full_name:nNTF {#1} \l__samcarter_file_name_tl
{ \__samcarter_comment_input: }
{ \msg_error:nnn { samcarter } { file-not-found } {#1} }
}
\msg_new:nnn { samcarter } { file-not-found } { File~'#1'~not~found. }
\cs_new_protected:Npn \__samcarter_comment_input:
{
\group_begin:
\cctab_select:N \c_other_cctab
\int_set:Nn \tex_endlinechar:D { \^^M }
\use:x { \tex_everyeof:D { \exp_not:N \q_nil \char_generate:nn { 13 } { 13 } } }
\char_set_active_eq:NN \^^M \__samcarter_process_line:w
\char_set_catcode_active:N \^^M
\iow_open:Nn \l__samcarter_iow { \c_sys_jobname_str . cif } % cif => comment input file
\exp_after:wN \__samcarter_process_line:w
\tex_input:D { \l__samcarter_file_name_tl }
\iow_close:N \l__samcarter_iow
\group_end:
\declare@file@substitution { \l__samcarter_file_name_tl } { \c_sys_jobname_str . cif }
\input { \l__samcarter_file_name_tl }
\undeclare@file@substitution { \l__samcarter_file_name_tl }
}
\group_begin:
\char_set_catcode_active:N \^^M
\char_set_active_eq:NN \^^M \scan_stop:
\cs_new_protected:Npx \__samcarter_process_line:w #1 ^^M
{ \exp_not:N \__samcarter_parse_line:w ^^M #1 ^^M \c_percent_str \s_stop }
\cs_set_protected:Npn \__samcarter_tmp:w #1
{
\cs_new_protected:Npn \__samcarter_parse_line:w ##1 ^^M #1 ##2 \s_stop
{
\tl_if_empty:nTF {##1}
{
\peek_charcode_remove:NTF #1
{ \__samcarter_ignore_line:w }
{ \__samcarter_output_line:w }
##2 \s_stop ^^M
}
{
\tl_set:Nx \l_tmpa_tl { \tl_tail:n {##1} }
\tl_if_eq:NNF \l_tmpa_tl \q_nil { ^^M }
}
}
\cs_new_protected:Npn \__samcarter_output_line:w ##1 ^^M #1 \s_stop
{ \iow_now:Nn \l__samcarter_iow {##1} }
\cs_new_protected:Npn \__samcarter_ignore_line:w ##1 \s_stop { }
}
\exp_args:No \__samcarter_tmp:w { \c_percent_str }
\group_end:
\makeatother
\ExplSyntaxOff

\begin{document}

some text

`