Skip to content

Commit

Permalink
feat: support for ANSI color sequences(#338)
Browse files Browse the repository at this point in the history
  • Loading branch information
nowhszh committed Jul 30, 2023
1 parent fe81965 commit 5487831
Show file tree
Hide file tree
Showing 16 changed files with 1,142 additions and 40 deletions.
3 changes: 1 addition & 2 deletions src/logdata/include/logdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,7 @@ class LogData : public AbstractLogData {

public:
klogg::vector<QString> decodeLines() const;
using OneLineLogConstructor
= std::function<OneLineLog( const char*, OneLineLog::OneLineLength )>;
using OneLineLogConstructor = std::function<OneLineLog( const char*, OneLineLog::Length )>;
klogg::vector<OneLineLog> splitLines( OneLineLogConstructor makeOneLineLog ) const;
klogg::vector<std::string_view> buildUtf8View() const;

Expand Down
17 changes: 13 additions & 4 deletions src/logdata/include/onelinelog.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,21 @@

class OneLineLog {
public:
using OneLineLength = QByteArray::size_type;
using Length = QByteArray::size_type;

public:
OneLineLog() = default;
OneLineLog( const char* data, OneLineLength len, std::shared_ptr<TextDecoder> dec,
std::shared_ptr<QRegularExpression> reg = {} );
OneLineLog( const char* data, Length len, std::shared_ptr<TextDecoder> dec,
std::shared_ptr<QRegularExpression> reg );

~OneLineLog() = default;

// no copying allowed
OneLineLog( const OneLineLog& ) = delete;
OneLineLog& operator=( const OneLineLog& ) = delete;

OneLineLog& operator=( OneLineLog&& ) = default;
OneLineLog( OneLineLog&& ) = default;

QString string();

Expand All @@ -28,7 +37,7 @@ class OneLineLog {

inline bool empty()
{
return buffer_.isEmpty();
return buffer_.isEmpty() || !decoder_ || !reg_;
}

private:
Expand Down
41 changes: 19 additions & 22 deletions src/logdata/src/logdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ QTextCodec* LogData::doGetDisplayEncoding() const
OneLineLog LogData::doGetOneLineLog( LineNumber line ) const
{
auto lines = doGetOneLineLogs( line, 1_lcount );
return lines.empty() ? OneLineLog{} : lines.front();
return lines.empty() ? OneLineLog{} : std::move( lines.front() );
}

klogg::vector<OneLineLog> LogData::doGetOneLineLogs( LineNumber firstLine, LinesCount number ) const
Expand All @@ -325,29 +325,29 @@ klogg::vector<OneLineLog> LogData::doGetOneLineLogs( LineNumber firstLine, Lines
}

klogg::vector<OneLineLog> processedLines;
std::shared_ptr<TextDecoder> dec( new TextDecoder( codec_.makeDecoder() ) );
std::shared_ptr<QRegularExpression> reg(
new QRegularExpression( prefilterPattern_, QRegularExpression::CaseInsensitiveOption ) );
std::shared_ptr<TextDecoder> dec{ std::make_shared<TextDecoder>( codec_.makeDecoder() ) };
std::shared_ptr<QRegularExpression> reg{ std::make_shared<QRegularExpression>(
prefilterPattern_, QRegularExpression::CaseInsensitiveOption ) };
try {
const auto rawLines = getLinesRaw( firstLine, number );
processedLines = rawLines.splitLines(
[ dec, reg ]( const char* log, OneLineLog::OneLineLength len ) -> OneLineLog {
return OneLineLog{ log, len, dec, reg };
[ &dec, &reg ]( const char* log, OneLineLog::Length len ) -> OneLineLog {
return { log, len, dec, reg };
} );

} catch ( const std::bad_alloc& e ) {
LOG_ERROR << "not enough memory " << e.what();
const char* replaceLog = "KLOGG WARNING: not enough memory";
processedLines.emplace_back(
replaceLog, type_safe::narrow_cast<OneLineLog::OneLineLength>( ::strlen( replaceLog ) ),
dec );
replaceLog, type_safe::narrow_cast<OneLineLog::Length>( ::strlen( replaceLog ) ), dec,
reg );
}

while ( processedLines.size() < number.get() ) {
const char* replaceLog = "KLOGG WARNING: failed to read some lines before this one";
processedLines.emplace_back(
replaceLog, type_safe::narrow_cast<OneLineLog::OneLineLength>( ::strlen( replaceLog ) ),
dec );
replaceLog, type_safe::narrow_cast<OneLineLog::Length>( ::strlen( replaceLog ) ), dec,
reg );
}

return processedLines;
Expand Down Expand Up @@ -561,46 +561,43 @@ LogData::RawLines::splitLines( OneLineLogConstructor makeOneLineLog ) const

try {
qint64 lineStart = 0;
size_t currentLineIndex = 0;
// size_t currentLineIndex = 0;
const auto lineFeedWidth = textDecoder.encodingParams.lineFeedWidth;
for ( const auto& lineEnd : this->endOfLines ) {
const auto length = lineEnd - lineStart - lineFeedWidth;
LOG_DEBUG << "line " << this->startLine.get() + currentLineIndex << ", length "
<< length;

constexpr auto maxlength = std::numeric_limits<int>::max() / 2;
if ( length >= maxlength ) {
const char* log = "KLOGG WARNING: this line is too long";
decodedLines.emplace_back( makeOneLineLog(
log, type_safe::narrow_cast<OneLineLog::OneLineLength>( ::strlen( log ) ) ) );
log, type_safe::narrow_cast<OneLineLog::Length>( ::strlen( log ) ) ) );
break;
}

if ( lineStart + length > klogg::ssize( buffer ) ) {
const char* log = "KLOGG WARNING: file read failed";
decodedLines.emplace_back( makeOneLineLog(
log, type_safe::narrow_cast<OneLineLog::OneLineLength>( ::strlen( log ) ) ) );
log, type_safe::narrow_cast<OneLineLog::Length>( ::strlen( log ) ) ) );
LOG_WARNING << "not enough data in buffer";
break;
}

decodedLines.emplace_back(
makeOneLineLog( buffer.data() + lineStart,
type_safe::narrow_cast<OneLineLog::OneLineLength>( length ) ) );
decodedLines.emplace_back( makeOneLineLog(
buffer.data() + lineStart, type_safe::narrow_cast<OneLineLog::Length>( length ) ) );

lineStart = lineEnd;
}
} catch ( const std::bad_alloc& ) {
LOG_ERROR << "not enough memory";
const char* log = "KLOGG WARNING: not enough memory";
decodedLines.emplace_back( makeOneLineLog(
log, type_safe::narrow_cast<OneLineLog::OneLineLength>( ::strlen( log ) ) ) );
decodedLines.emplace_back(
makeOneLineLog( log, type_safe::narrow_cast<OneLineLog::Length>( ::strlen( log ) ) ) );
}

while ( decodedLines.size() < this->endOfLines.size() ) {
const char* log = "KLOGG WARNING: failed to decode some lines before this one";
decodedLines.emplace_back( makeOneLineLog(
log, type_safe::narrow_cast<OneLineLog::OneLineLength>( ::strlen( log ) ) ) );
decodedLines.emplace_back(
makeOneLineLog( log, type_safe::narrow_cast<OneLineLog::Length>( ::strlen( log ) ) ) );
}

return decodedLines;
Expand Down
17 changes: 15 additions & 2 deletions src/logdata/src/onelinelog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#include "linetypes.h"

OneLineLog::OneLineLog( const char* data, OneLineLength len, std::shared_ptr<TextDecoder> dec,
OneLineLog::OneLineLog( const char* data, OneLineLog::Length len, std::shared_ptr<TextDecoder> dec,
std::shared_ptr<QRegularExpression> reg )
: buffer_( data, len )
, decoder_( dec )
Expand All @@ -18,7 +18,9 @@ OneLineLog::OneLineLog( const char* data, OneLineLength len, std::shared_ptr<Tex

QString OneLineLog::string()
{
if ( buffer_.isEmpty() ) {
if ( empty() ) {
LOG_WARNING << buffer_.isEmpty() << ",count:" << decoder_.use_count() << ","
<< reg_.use_count();
return {};
}
auto log = decoder_->decoder->toUnicode( buffer_ );
Expand All @@ -35,6 +37,11 @@ QString OneLineLog::string()

QString OneLineLog::expandedString()
{
if ( empty() ) {
LOG_INFO << buffer_.isEmpty() << ",count:" << decoder_.use_count() << ","
<< reg_.use_count();
return {};
}
auto log = decoder_->decoder->toUnicode( buffer_ );

if ( reg_->isValid() ) {
Expand All @@ -46,6 +53,12 @@ QString OneLineLog::expandedString()

QString OneLineLog::process( std::function<void( QString& )> fn )
{
if ( empty() ) {
LOG_INFO << buffer_.isEmpty() << ",count:" << decoder_.use_count() << ","
<< reg_.use_count();
return {};
}

auto log = decoder_->decoder->toUnicode( buffer_.data(),
type_safe::narrow_cast<int>( buffer_.size() ) );

Expand Down
13 changes: 12 additions & 1 deletion src/settings/include/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,15 @@ class Configuration final : public Persistable<Configuration> {
hideAnsiColorSequences_ = hide;
}

bool displayAnsiColorSequences() const
{
return displayAnsiColorSequences_;
}
void setDisplayAnsiColorSequences( bool display )
{
displayAnsiColorSequences_ = display;
}

int defaultEncodingMib() const
{
return defaultEncodingMib_;
Expand All @@ -500,7 +509,8 @@ class Configuration final : public Persistable<Configuration> {
defaultEncodingMib_ = mib;
}

std::map<QString, QString> darkPalette() const {
std::map<QString, QString> darkPalette() const
{
return darkPalette_;
}

Expand Down Expand Up @@ -581,6 +591,7 @@ class Configuration final : public Persistable<Configuration> {
bool optimizeForNotLatinEncodings_ = false;

bool hideAnsiColorSequences_ = false;
bool displayAnsiColorSequences_ = false;

int defaultEncodingMib_ = -1;

Expand Down
9 changes: 7 additions & 2 deletions src/settings/src/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ void Configuration::retrieveFromStorage( QSettings& settings )
= settings
.value( "view.hideAnsiColorSequences", DefaultConfiguration.hideAnsiColorSequences_ )
.toBool();
displayAnsiColorSequences_ = settings
.value( "view.displayAnsiColorSequences",
DefaultConfiguration.displayAnsiColorSequences_ )
.toBool();

useTextWrap_ = settings.value( "view.textWrap", DefaultConfiguration.useTextWrap() ).toBool();

Expand Down Expand Up @@ -300,7 +304,7 @@ void Configuration::retrieveFromStorage( QSettings& settings )
const auto mapping = settings.value( "shortcuts.mapping" ).toMap();
for ( auto keys = mapping.begin(); keys != mapping.end(); ++keys ) {
auto action = keys.key().toStdString();
if (action == ShortcutAction::LogViewJumpToButtom) {
if ( action == ShortcutAction::LogViewJumpToButtom ) {
action = ShortcutAction::LogViewJumpToBottom;
}
shortcuts_.emplace( action, keys.value().toStringList() );
Expand All @@ -314,7 +318,7 @@ void Configuration::retrieveFromStorage( QSettings& settings )
settings.setArrayIndex( static_cast<int>( shortcutIndex ) );
auto action = settings.value( "action", "" ).toString();
if ( !action.isEmpty() ) {
if (action == ShortcutAction::LogViewJumpToButtom) {
if ( action == ShortcutAction::LogViewJumpToButtom ) {
action = ShortcutAction::LogViewJumpToBottom;
}
const auto keys = settings.value( "keys", QStringList() ).toStringList();
Expand Down Expand Up @@ -395,6 +399,7 @@ void Configuration::saveToStorage( QSettings& settings ) const
settings.setValue( "view.scaleFactorRounding", scaleFactorRounding_ );

settings.setValue( "view.hideAnsiColorSequences", hideAnsiColorSequences_ );
settings.setValue( "view.displayAnsiColorSequences", displayAnsiColorSequences_ );

settings.setValue( "defaultView.searchAutoRefresh", searchAutoRefresh_ );
settings.setValue( "defaultView.searchIgnoreCase", searchIgnoreCase_ );
Expand Down
5 changes: 5 additions & 0 deletions src/ui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ add_library(
${CMAKE_CURRENT_SOURCE_DIR}/include/decompressor.h
${CMAKE_CURRENT_SOURCE_DIR}/include/fontutils.h
${CMAKE_CURRENT_SOURCE_DIR}/include/colorlabelsmanager.h
${CMAKE_CURRENT_SOURCE_DIR}/include/ANSI.h
${CMAKE_CURRENT_SOURCE_DIR}/include/SGRParser.h
${CMAKE_CURRENT_SOURCE_DIR}/include/ColorfulTextParser.h
${CMAKE_CURRENT_SOURCE_DIR}/include/highlighteredit.ui
${CMAKE_CURRENT_SOURCE_DIR}/include/highlightersetedit.ui
${CMAKE_CURRENT_SOURCE_DIR}/include/highlightersdialog.ui
Expand Down Expand Up @@ -91,6 +94,8 @@ add_library(
${CMAKE_CURRENT_SOURCE_DIR}/src/downloader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/decompressor.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/colorlabelsmanager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/SGRParser.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ColorfulTextParser.cpp
)

set_target_properties(klogg_ui PROPERTIES AUTOUIC ON)
Expand Down
94 changes: 94 additions & 0 deletions src/ui/include/ANSI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//
// Created by marvin on 23-7-18.
//
#pragma once

#include <cstdint>

namespace ANSI {

enum SequenceStartCnt {
HEAD_CNT = 2,
};

// reference: https://www.ecma-international.org/publications-and-standards/standards/ecma-48/
enum SequenceFirst {
EXC = 0x1B, // Escape(转义)
};

// reference: https://www.ecma-international.org/publications-and-standards/standards/ecma-48/
enum SequenceSecond {
PAD = 0x40, // 0x80 Padding Character(填充字符)
HOP = 0x41, // 0x81 High Octet Preset(高字节前置)
BPH = 0x42, // 0x82 Break Permitted Here(此处允许中断)
NBH = 0x43, // 0x83 No Break Here(此处禁止中断)
IND = 0x44, // 0x84 Index(索引)
NEL = 0x45, // 0x85 Next Line(下一行)
SSA = 0x46, // 0x86 Start of Selected Area(选择区域开始)
ESA = 0x47, // 0x87 End of Selected Area(选择区域结束)
HTS = 0x48, // 0x88 Horizontal Tab Set(水平制表设置)
HTJ = 0x49, // 0x89 Horizontal Tab Justified(水平制表调整)
VTS = 0x4A, // 0x8A Vertical Tab Set(垂直制表设置)
PLD = 0x4B, // 0x8B Partial Line Forward(部分行前移)
PLU = 0x4C, // 0x8C Partial Line Backward(部分行后移)
RI = 0x4D, // 0x8D Reverse Line Feed(逆向馈行)
SS2 = 0x4E, // 0x8E Single-Shift 2(单个移动2)
SS3 = 0x4F, // 0x8F Single-Shift 3(单个移动3)
DCS = 0x50, // 0x90 Device Control String(设备控制串)
PU1 = 0x51, // 0x91 Private Use 1(私用1)
PU2 = 0x52, // 0x92 Private Use 2(私用2)
STS = 0x53, // 0x93 Set Transmit State(发送规则设置)
CCH = 0x54, // 0x94 Cancel Character(取消字符)
MW = 0x55, // 0x95 Message Waiting(消息等待)
SPA = 0x56, // 0x96 Start of Protected Area(保护区域开始)
EPA = 0x57, // 0x97 End of Protected Area(保护区域结束)
SOS = 0x58, // 0x98 Start of String(串开始)
SGC = 0x59, // 0x99 Single Graphic Char Intro(单个图形字符描述)
SCI = 0x5A, // 0x9A Single Char Intro(单个字符描述)
CSI = 0x5B, // 0x9B Control Sequence Intro(控制顺序描述)
ST = 0x5C, // 0x9C String Terminator(串终止)
OSC = 0x5D, // 0x9D OS Command(操作系统指令)
PM = 0x5E, // 0x9E Private Message(私讯)
APC = 0x5F, // 0x9F App Program Command(应用程序命令)

SECOND_BYTE_BEGIN = PAD,
SECOND_BYTE_END = APC,
};

// reference: https://www.ecma-international.org/publications-and-standards/standards/ecma-48/
enum CSIParameterBytes {
// ASCII: 0–9:;<=>?
NUM_BEGIN = 0x30,
NUM_END = 0x39,
SUB_PARA_SEPARATOR = 0x3A,
PARA_SEPARATOR = 0x3B,
STANDARDIZATION_KEEP_BEGIN = 0x3C,
STANDARDIZATION_KEEP_END = 0x3F,

CSI_PARAMETER_BEGIN = 0x30,
CSI_PARAMETER_END = 0x3F,
};

enum CSIIntermediateBytes {
// ASCII: Space、!"#$%&'()*+,-./
CSIIntermediateBegin = 0x20,
CSIIntermediateEnd = 0x2F,
};

enum CSIFinalBytes {
// ASCII: @A–Z[\]^_`a–z{|}~
SGR = 0x6D, // m

CSIFinalBegin = 0x40,
CSIFinalEnd = 0x7E,

CSIFinalExperimentalBegin = 0x70,
CSIFinalExperimentalEnd = CSIFinalEnd,
};

enum class Return {
PARSE_ERROR = -1,
PARSE_SUCC = 0,
};

} // namespace ANSI
Loading

0 comments on commit 5487831

Please sign in to comment.