From 36e8a6529d6335a04a6924de28d1c0b405f0966f Mon Sep 17 00:00:00 2001 From: Chris Jefferson Date: Wed, 19 Oct 2022 18:48:27 +0200 Subject: [PATCH] Extend StreamFormatting to seperate linewrap and indenting --- lib/custom_streams.gi | 9 +-- lib/streams.gd | 4 +- lib/streams.gi | 47 +++++++----- src/io.c | 170 ++++++++++++++++++++++++++---------------- src/io.h | 22 +++++- 5 files changed, 159 insertions(+), 93 deletions(-) diff --git a/lib/custom_streams.gi b/lib/custom_streams.gi index 01798f9ddc0..7d76888b29a 100644 --- a/lib/custom_streams.gi +++ b/lib/custom_streams.gi @@ -343,11 +343,8 @@ InstallMethod( PrintFormattingStatus, "output text custom", ## InstallMethod( SetPrintFormattingStatus, "output text custom", [IsOutputTextCustomRep and IsOutputTextStream, - IsBool], + IsObject], function( str, stat) - if stat = fail then - Error("Print formatting status must be true or false"); - else - str!.formatting := stat; - fi; + CheckValidPrintFormattingStatus(stat); + str!.formatting := stat; end); diff --git a/lib/streams.gd b/lib/streams.gd index 4833b8dd439..7513df0e499 100644 --- a/lib/streams.gd +++ b/lib/streams.gd @@ -1007,7 +1007,7 @@ DeclareGlobalFunction( "InputOutputLocalProcess" ); ## ## <#/GAPDoc> ## -DeclareOperation( "SetPrintFormattingStatus", [IsOutputStream, IsBool] ); +DeclareOperation( "SetPrintFormattingStatus", [IsOutputStream, IsObject] ); DeclareOperation( "PrintFormattingStatus", [IsOutputStream] ); @@ -1251,3 +1251,5 @@ DeclareGlobalFunction( "InputFromUser" ); ## <#/GAPDoc> ## DeclareGlobalFunction( "OpenExternal" ); + +DeclareGlobalFunction( "CheckValidPrintFormattingStatus"); diff --git a/lib/streams.gi b/lib/streams.gi index 7a4b2077973..4505919a40c 100644 --- a/lib/streams.gi +++ b/lib/streams.gi @@ -929,13 +929,10 @@ InstallMethod( PrintFormattingStatus, "output text string", ## InstallMethod( SetPrintFormattingStatus, "output text string", [IsOutputTextStringRep and IsOutputTextStream, - IsBool], + IsObject], function( str, stat) - if stat = fail then - Error("Print formatting status must be true or false"); - else - str![2] := stat; - fi; + CheckValidPrintFormattingStatus(stat); + str![2] := stat; end); @@ -1130,13 +1127,10 @@ InstallMethod( PrintFormattingStatus, "output text file", ## InstallMethod( SetPrintFormattingStatus, "output text file", [IsOutputTextFileRep and IsOutputTextStream, - IsBool], + IsObject], function( str, stat) - if stat = fail then - Error("Print formatting status must be true or false"); - else - str![3] := stat; - fi; + CheckValidPrintFormattingStatus(stat); + str![3] := stat; end); ## formatting status for stdout or current output @@ -1151,7 +1145,7 @@ function(str) fi; end); -InstallOtherMethod( SetPrintFormattingStatus, "for stdout", [IsString, IsBool], +InstallOtherMethod( SetPrintFormattingStatus, "for stdout", [IsString, IsObject], function(str, status) if str = "*stdout*" then SET_PRINT_FORMATTING_STDOUT(status); @@ -1255,9 +1249,7 @@ InstallMethod( SetPrintFormattingStatus, "output text none", [IsOutputTextNoneRep and IsOutputTextNone, IsBool], function( str, stat) - if stat = fail then - Error("Print formatting status must be true or false"); - fi; + CheckValidPrintFormattingStatus(stat); end); @@ -1676,14 +1668,29 @@ InstallMethod( SetPrintFormattingStatus, "for non-text output stream", TryNextMethod(); fi; - if stat = true then - Error("non-text streams support onlyPrint formatting status false"); - elif stat = fail then - Error("Print formatting status must be true or false"); + CheckValidPrintFormattingStatus(stat); + if (( IsBool(stat) and stat = true ) or + ( IsRecord(stat) and (stat.linewrap or stat.indent) ) ) then + Error("non-text streams do not support print formatting"); fi; end); +InstallGlobalFunction( "CheckValidPrintFormattingStatus", + function(fs) + if IsBool(fs) then + if fs = fail then + Error("Formatting status cannot be 'fail'"); + fi; + elif IsRecord(fs) then + if Set(RecNames(fs)) <> ["indent", "linewrap"] then + Error("Formatting status records must only contain 'indent' and 'linewarp'"); + fi; + else + Error("Formatting status must be a Boolean or a Record"); + fi; +end); + ############################################################################# ## #M FileDescriptorOfStream( ) diff --git a/src/io.c b/src/io.c index f81f911373b..141dd43f53a 100644 --- a/src/io.c +++ b/src/io.c @@ -32,7 +32,9 @@ #include "lists.h" #include "modules.h" #include "plist.h" +#include "precord.h" #include "read.h" +#include "records.h" #include "scanner.h" #include "stringobj.h" #include "symbols.h" @@ -113,8 +115,8 @@ struct IOModuleState { Int NoSplitLine; - BOOL PrintFormattingForStdout; - BOOL PrintFormattingForErrout; + StreamFormat PrintFormattingForStdout; + StreamFormat PrintFormattingForErrout; }; // for debugging from GDB / lldb, we mark this as extern inline @@ -849,7 +851,7 @@ UInt OpenOutput(TypOutputFile * output, const Char * filename, BOOL append) else if (streq(filename, "*errout*")) output->format = IO()->PrintFormattingForErrout; else - output->format = TRUE; + output->format = MakeStreamFormat(1, 1); output->indent = 0; /* variables related to line splitting, very bad place to split */ @@ -884,7 +886,8 @@ UInt OpenOutputStream(TypOutputFile * output, Obj stream) output->file = -1; output->line[0] = '\0'; output->pos = 0; - output->format = (CALL_1ARGS(PrintFormattingStatus, stream) == True); + output->format = + StreamFormatFromObj(CALL_1ARGS(PrintFormattingStatus, stream)); output->indent = 0; /* variables related to line splitting, very bad place to split */ @@ -1234,21 +1237,21 @@ static void PutChrTo(TypOutputFile * stream, Char ch) /* '\01', increment indentation level */ if ( ch == '\01' ) { - if (!stream->format) - return; + if (NoStreamFormat(stream->format)) + return; - /* add hint to break line */ - addLineBreakHint(stream, stream->pos, 16*stream->indent, 1); + /* add hint to break line */ + addLineBreakHint(stream, stream->pos, 16 * stream->indent, 1); } /* '\02', decrement indentation level */ else if ( ch == '\02' ) { - if (!stream->format) - return; + if (NoStreamFormat(stream->format)) + return; - /* if this is a better place to split the line remember it */ - addLineBreakHint(stream, stream->pos, 16*stream->indent, -1); + /* if this is a better place to split the line remember it */ + addLineBreakHint(stream, stream->pos, 16 * stream->indent, -1); } /* '\03', print line */ @@ -1280,12 +1283,11 @@ static void PutChrTo(TypOutputFile * stream, Char ch) /* and dump it from the buffer */ stream->pos = 0; - if (stream->format) - { + if (stream->format.indent) { /* indent for next line */ for ( i = 0; i < stream->indent; i++ ) stream->line[ stream->pos++ ] = ' '; - } + } /* reset line break hints */ stream->hints[0] = -1; @@ -1316,55 +1318,57 @@ static void PutChrTo(TypOutputFile * stream, Char ch) /* if we are going to split at the end of the line, and we are formatting discard blanks */ - if ( stream->format && spos == stream->pos && ch == ' ' ) { - ; + if (stream->format.linewrap && spos == stream->pos && ch == ' ') { + ; } /* full line, acceptable split position */ - else if ( stream->format && spos != 0 ) { - - /* add character to the line, terminate it */ - stream->line[ stream->pos++ ] = ch; - stream->line[ stream->pos++ ] = '\0'; - - /* copy the rest after the best split position to a safe place */ - for ( i = spos; i < stream->pos; i++ ) - str[ i-spos ] = stream->line[ i ]; - str[ i-spos] = '\0'; - - /* print line up to the best split position */ - stream->line[ spos++ ] = '\n'; - stream->line[ spos ] = '\0'; - PutLineTo( stream, spos ); - spos--; + else if (stream->format.linewrap && spos != 0) { + + /* add character to the line, terminate it */ + stream->line[stream->pos++] = ch; + stream->line[stream->pos++] = '\0'; + + /* copy the rest after the best split position to a safe place */ + for (i = spos; i < stream->pos; i++) + str[i - spos] = stream->line[i]; + str[i - spos] = '\0'; + + /* print line up to the best split position */ + stream->line[spos++] = '\n'; + stream->line[spos] = '\0'; + PutLineTo(stream, spos); + spos--; + + stream->pos = 0; + if (stream->format.indent) { + /* indent for the rest */ + for (i = 0; i < stream->hints[3 * hint + 2]; i++) + stream->line[stream->pos++] = ' '; + spos -= stream->hints[3 * hint + 2]; + } - /* indent for the rest */ - stream->pos = 0; - for ( i = 0; i < stream->hints[3*hint+2]; i++ ) - stream->line[ stream->pos++ ] = ' '; - spos -= stream->hints[3*hint+2]; - - /* copy the rest onto the next line */ - for ( i = 0; str[ i ] != '\0'; i++ ) - stream->line[ stream->pos++ ] = str[ i ]; - /* recover line break hints for copied rest */ - for ( i = hint+1; stream->hints[3*i] != -1; i++ ) - { - stream->hints[3*(i-hint-1)] = stream->hints[3*i]-spos; - stream->hints[3*(i-hint-1)+1] = stream->hints[3*i+1]; - stream->hints[3*(i-hint-1)+2] = stream->hints[3*i+2]; - } - stream->hints[3*(i-hint-1)] = -1; + /* copy the rest onto the next line */ + for (i = 0; str[i] != '\0'; i++) + stream->line[stream->pos++] = str[i]; + /* recover line break hints for copied rest */ + for (i = hint + 1; stream->hints[3 * i] != -1; i++) { + stream->hints[3 * (i - hint - 1)] = stream->hints[3 * i] - spos; + stream->hints[3 * (i - hint - 1) + 1] = + stream->hints[3 * i + 1]; + stream->hints[3 * (i - hint - 1) + 2] = + stream->hints[3 * i + 2]; + } + stream->hints[3 * (i - hint - 1)] = -1; } /* full line, no split position */ else { - if (stream->format) - { - /* append a '\',*/ - stream->line[ stream->pos++ ] = '\\'; - stream->line[ stream->pos++ ] = '\n'; + if (stream->format.linewrap) { + /* append a '\',*/ + stream->line[stream->pos++] = '\\'; + stream->line[stream->pos++] = '\n'; } /* and print the line */ stream->line[ stream->pos ] = '\0'; @@ -1374,8 +1378,8 @@ static void PutChrTo(TypOutputFile * stream, Char ch) stream->pos = 0; stream->line[ stream->pos++ ] = ch; - if (stream->format) - stream->hints[0] = -1; + if (stream->format.indent) + stream->hints[0] = -1; } } @@ -1881,6 +1885,42 @@ void SPrTo(Char *buffer, UInt maxlen, const Char *format, Int arg1, Int arg2) } +// Convert GAP-level print formatting objects to kernel level, and vice-versa + +StreamFormat StreamFormatFromObj(Obj o) +{ + if (o == True) { + return MakeStreamFormat(1, 1); + } + if (o == False) { + return MakeStreamFormat(0, 0); + } + + RequirePlainRec("Stream formatting", o); + + Obj indent = ElmPRec(o, RNamName("indent")); + Obj linewrap = ElmPRec(o, RNamName("linewrap")); + + RequireTrueOrFalse("indent in stream formatting", indent); + RequireTrueOrFalse("linewrap in stream formatting", linewrap); + + BOOL i = (indent == True); + BOOL l = (linewrap == True); + + return MakeStreamFormat(l, i); +} + +Obj ObjFromStreamFormat(StreamFormat sf) +{ + Obj o = NEW_PREC(0); + Obj indent = sf.indent ? True : False; + Obj linewrap = sf.linewrap ? True : False; + AssPRec(o, RNamName("indent"), indent); + AssPRec(o, RNamName("linewrap"), linewrap); + return o; +} + + static Obj FuncINPUT_FILENAME(Obj self) { if (IO()->Input == 0) @@ -1897,7 +1937,7 @@ static Obj FuncINPUT_LINENUMBER(Obj self) static Obj FuncSET_PRINT_FORMATTING_STDOUT(Obj self, Obj val) { - BOOL format = (val != False); + StreamFormat format = StreamFormatFromObj(val); TypOutputFile * output = IO()->Output; while (output) { if (!output->stream && output->file == 1) @@ -1910,12 +1950,12 @@ static Obj FuncSET_PRINT_FORMATTING_STDOUT(Obj self, Obj val) static Obj FuncPRINT_FORMATTING_STDOUT(Obj self) { - return IO()->PrintFormattingForStdout ? True : False; + return ObjFromStreamFormat(IO()->PrintFormattingForStdout); } static Obj FuncSET_PRINT_FORMATTING_ERROUT(Obj self, Obj val) { - BOOL format = (val != False); + StreamFormat format = StreamFormatFromObj(val); TypOutputFile * output = IO()->Output; while (output) { if (!output->stream && output->file == 3) @@ -1928,7 +1968,7 @@ static Obj FuncSET_PRINT_FORMATTING_ERROUT(Obj self, Obj val) static Obj FuncPRINT_FORMATTING_ERROUT(Obj self) { - return IO()->PrintFormattingForErrout ? True : False; + return ObjFromStreamFormat(IO()->PrintFormattingForErrout); } /**************************************************************************** @@ -1947,8 +1987,8 @@ static Obj FuncCALL_WITH_FORMATTING_STATUS(Obj self, Obj status, Obj func, Obj a if (!output) ErrorMayQuit("CALL_WITH_FORMATTING_STATUS called while no output is open", 0, 0); - BOOL old = output->format; - output->format = (status != False); + StreamFormat old = output->format; + output->format = StreamFormatFromObj(status); Obj result; GAP_TRY @@ -2029,8 +2069,8 @@ static Int InitKernel ( IO()->Output = 0; IO()->InputLog = 0; IO()->OutputLog = 0; - IO()->PrintFormattingForStdout = TRUE; - IO()->PrintFormattingForErrout = TRUE; + IO()->PrintFormattingForStdout = MakeStreamFormat(1, 1); + IO()->PrintFormattingForErrout = MakeStreamFormat(1, 1); OpenOutput(&IO()->DefaultOutput, "*stdout*", FALSE); diff --git a/src/io.h b/src/io.h index 78e0b7ca07d..65301fb811c 100644 --- a/src/io.h +++ b/src/io.h @@ -92,6 +92,26 @@ enum { MAXLENOUTPUTLINE = 4096, }; +typedef struct { + unsigned linewrap : 1; + unsigned indent : 1; +} StreamFormat; + +EXPORT_INLINE StreamFormat MakeStreamFormat(unsigned linewrap, + unsigned indent) +{ + StreamFormat s = { .linewrap = linewrap, .indent = indent }; + return s; +} + +EXPORT_INLINE BOOL NoStreamFormat(StreamFormat sf) +{ + return sf.linewrap == 0 && sf.indent == 0; +} + + +StreamFormat StreamFormatFromObj(Obj o); +Obj ObjFromStreamFormat(StreamFormat sf); /**************************************************************************** ** @@ -113,7 +133,7 @@ struct TypOutputFile { char line[MAXLENOUTPUTLINE]; Int pos; - BOOL format; + StreamFormat format; Int indent; /* each hint is a triple (position, value, indent) */