From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from EUR01-DB5-obe.outbound.protection.outlook.com (mail-db5eur01on2083.outbound.protection.outlook.com [40.107.15.83]) by sourceware.org (Postfix) with ESMTPS id 2F1D03858CD1 for ; Fri, 23 Jun 2023 11:52:31 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 2F1D03858CD1 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=arm.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=armh.onmicrosoft.com; s=selector2-armh-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=XDanEs1ne3gdvXooBPp0IoB4d9+giS65DwrbaZ5fpLA=; b=GG6QWBYsCS5RlJvM8KJJ+xlrJj/MoKHlc93aV8l6j8Kb/RGtP2L/JvAAsX1I3c6dQqxZzvfWzeD2uanLTs1ZSHhSe51yH1NfoarDW5biUaslv0p8V9IX6B73sOWrOkrlaXyK3xtY6VhD5tCgJXUihk0sjVqYFnib3+Y2o2phGwA= Received: from DUZPR01CA0250.eurprd01.prod.exchangelabs.com (2603:10a6:10:4b5::6) by AS8PR08MB9930.eurprd08.prod.outlook.com (2603:10a6:20b:564::18) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6521.26; Fri, 23 Jun 2023 11:52:22 +0000 Received: from DBAEUR03FT060.eop-EUR03.prod.protection.outlook.com (2603:10a6:10:4b5:cafe::fa) by DUZPR01CA0250.outlook.office365.com (2603:10a6:10:4b5::6) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6521.26 via Frontend Transport; Fri, 23 Jun 2023 11:52:21 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 63.35.35.123) smtp.mailfrom=arm.com; dkim=pass (signature was verified) header.d=armh.onmicrosoft.com;dmarc=pass action=none header.from=arm.com; Received-SPF: Pass (protection.outlook.com: domain of arm.com designates 63.35.35.123 as permitted sender) receiver=protection.outlook.com; client-ip=63.35.35.123; helo=64aa7808-outbound-1.mta.getcheckrecipient.com; pr=C Received: from 64aa7808-outbound-1.mta.getcheckrecipient.com (63.35.35.123) by DBAEUR03FT060.mail.protection.outlook.com (100.127.142.238) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6521.24 via Frontend Transport; Fri, 23 Jun 2023 11:52:21 +0000 Received: ("Tessian outbound 52217515e112:v142"); Fri, 23 Jun 2023 11:52:21 +0000 X-CheckRecipientChecked: true X-CR-MTA-CID: ade19a3dff588915 X-CR-MTA-TID: 64aa7808 Received: from cfd73fdd9920.1 by 64aa7808-outbound-1.mta.getcheckrecipient.com id 95A9D7B0-5BDE-49B4-BC22-8BCCF8C623F2.1; Fri, 23 Jun 2023 11:52:14 +0000 Received: from EUR05-VI1-obe.outbound.protection.outlook.com by 64aa7808-outbound-1.mta.getcheckrecipient.com with ESMTPS id cfd73fdd9920.1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384); Fri, 23 Jun 2023 11:52:14 +0000 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=OE7cEfhNPZ/i+NSFrtrHLvvrgrE1xHAKjmlsiU+fsn+TkZyvPy2I/6Kv0ULKki+5uD3z4Noi7N2SsQRJLegfDgKcSK8zSGqMnD+2o/UbJuYvK8So03JOXeD32Q5Iis1KlEReypYnBKrek948M4pXAJ8HIQg/GQIzWJPhTgZ+gU9LXQmc+KRt1MVm63Ty7NJ0hun3B/2P/tKuPxbf3MiXD/Cn8Vu6rTmdSa0VAqH9mmnWjniADnL/paY4xatq63TTOE3N/qJ6QnMrdtqw+rkShTaQnrDdc3CZx3jMlX+tigYvtjXgKJIwDeN2mWOQgaAgRlF8Yk1DRzR+O3FZj+JU5g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=XDanEs1ne3gdvXooBPp0IoB4d9+giS65DwrbaZ5fpLA=; b=QW/prznIfBa2RRJa3b4A0GTVXlGqPE6nx3qdyPqgH53F9OGG3TePMKGu17AyvEZ0DljhGm8DpnJBr1FTlpvbTtey50IBn1q6q5ssT1D48fnqTvihYx27oQ7UnIZnpzk1iWnWwdFHz6EFhAayb3PW4nDzXnXkqw61SDRIoIyxPRWf8mRLjCTvBPfpsax9UjAGmWcJ3CoMVbaLq+faVKuCrKhUe68ngd22GASyjrt8nYiHLZ54X55ngXYb3COZOSBia235T+hhOEh3CRXrfeUCNtkR/jXPamsjhHo7xmrppIL1zKvPCBq0Lck9QF/qk9MEDRios1/orHeSPk29D9eTZQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=arm.com; dmarc=pass action=none header.from=arm.com; dkim=pass header.d=arm.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=armh.onmicrosoft.com; s=selector2-armh-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=XDanEs1ne3gdvXooBPp0IoB4d9+giS65DwrbaZ5fpLA=; b=GG6QWBYsCS5RlJvM8KJJ+xlrJj/MoKHlc93aV8l6j8Kb/RGtP2L/JvAAsX1I3c6dQqxZzvfWzeD2uanLTs1ZSHhSe51yH1NfoarDW5biUaslv0p8V9IX6B73sOWrOkrlaXyK3xtY6VhD5tCgJXUihk0sjVqYFnib3+Y2o2phGwA= Authentication-Results-Original: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=arm.com; Received: from PAWPR08MB8958.eurprd08.prod.outlook.com (2603:10a6:102:33e::15) by DU0PR08MB7462.eurprd08.prod.outlook.com (2603:10a6:10:355::15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6521.26; Fri, 23 Jun 2023 11:52:05 +0000 Received: from PAWPR08MB8958.eurprd08.prod.outlook.com ([fe80::202a:45b7:3be8:47d5]) by PAWPR08MB8958.eurprd08.prod.outlook.com ([fe80::202a:45b7:3be8:47d5%7]) with mapi id 15.20.6521.024; Fri, 23 Jun 2023 11:52:05 +0000 Date: Fri, 23 Jun 2023 12:52:01 +0100 From: Alex Coplan To: David Malcolm Cc: gcc-patches@gcc.gnu.org Subject: Re: [PATCH 2/3] diagnostics: add support for "text art" diagrams Message-ID: References: <20230531180630.3127108-1-dmalcolm@redhat.com> <20230531180630.3127108-3-dmalcolm@redhat.com> Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable In-Reply-To: <20230531180630.3127108-3-dmalcolm@redhat.com> X-ClientProxiedBy: LO4P123CA0686.GBRP123.PROD.OUTLOOK.COM (2603:10a6:600:37b::18) To PAWPR08MB8958.eurprd08.prod.outlook.com (2603:10a6:102:33e::15) MIME-Version: 1.0 X-MS-TrafficTypeDiagnostic: PAWPR08MB8958:EE_|DU0PR08MB7462:EE_|DBAEUR03FT060:EE_|AS8PR08MB9930:EE_ X-MS-Office365-Filtering-Correlation-Id: 32d2f44b-09de-4ef2-ad3a-08db73e04dde x-checkrecipientrouted: true NoDisclaimer: true X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam-Untrusted: BCL:0; X-Microsoft-Antispam-Message-Info-Original: 0nK/PPsWn0/De/rgR61zzyKTDInGxS7FxZq2PAXCLpAvZTt2CpEF/PPBQhT7pB8rctNb0Z7//2YdyQUGboVnCGPo1djEDi3c77b5Biago5d29p/Sqe8tolQ/D4v/TSu1DNqFeaFN4HdUUqjqHQ+LGJOTvLaYdTjDM+FDdUhPkWkfZSTR83Tmq9WFecpNo56Tc8SgP60nOWKR81ODeoKIyVvK7mBk2Nja4iyVRs8Mi5A6OejwPIkAb5UpyhqtMl5zRresUEMiLbvw3kVqBqOagKxRPzq5Vxg1t1cRgaEwKQwiHpkvfzNWduwQXvQynSAqw25+95Sc9XwGyOypMD2M7TQm09F9dA4P5GdAOe952xOvdFPSaNP4PHj8syxIw8ogMIyySRUXPuKvUi6A0cVupMS7V/reMojC+HHrn2v++BoYO/YJmwahHHwdUUfZ8n4tT7mxrDyRFZ+bf8u7pDhTQMkhoRTQrMkOEoE4x+S3JtccYSxcSwAv1p5bIR3Fb3h1h67ogtbF0WLtGuGhFjIvshOqbzxtL6kTzSbM4fvr76rsQoqeltko15b9Je+gaVCKikxMqM96o9qG1lW6tuHp3xE0kInteyiDh9RNqNKx6fpOtp0W8Q2U9NTR7DaBRfh6S0AYi00FJ2MFODOr3EEqodtHu/ohs4X5znoDtWEkotI= X-Forefront-Antispam-Report-Untrusted: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PAWPR08MB8958.eurprd08.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230028)(4636009)(396003)(376002)(136003)(346002)(366004)(39860400002)(451199021)(84970400001)(6666004)(478600001)(4326008)(2616005)(86362001)(83380400001)(36756003)(2906002)(6486002)(186003)(30864003)(26005)(53546011)(6506007)(966005)(66946007)(6512007)(38100700002)(66556008)(66476007)(5660300002)(316002)(8936002)(41300700001)(8676002)(6916009)(44832011)(2004002)(15398625002)(579004)(559001);DIR:OUT;SFP:1101; X-MS-Exchange-Transport-CrossTenantHeadersStamped: DU0PR08MB7462 Original-Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=arm.com; X-EOPAttributedMessage: 0 X-MS-Exchange-Transport-CrossTenantHeadersStripped: DBAEUR03FT060.eop-EUR03.prod.protection.outlook.com X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id-Prvs: d61e9ddc-9c95-49c6-c437-08db73e0438c X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: U4if0QEuZ+JPXWZo7XqWCR+C51cA7G66xvSbA4eaBagui7na3dwdMn7NlHJjGHwgqWajJrmCHgihKsl8JAiinePuCxiLJsluFmEAJqfUDZRknFH3o7dVKPW5xAIk9Xfc2TWr7q4fmbKQ6O2bkBwCVmsHCyQq+bFJJ5CuYY1f5B9Ipjsrv+oCotqZVhM7+6DtQhbLT/BqzAX0BbhHwNwXNJUAGZgPgiNvf6CmC5Bqt4gJhdVm3WOsyuxtHbgYtY3unZXutCLsPhotToUngsRcFGxg+tVRsQrksZJPqEKYdUcB/BykjC6cfiHsvHjNuGr+7AMCuSKWOia7BN4JamzWD0VOjpF1myK0wDQBguVE8HOrWACdclRIS3v94UdBjijBOviSqTd7/HLADqRI8LsNjyWx07+QxY+zWsQeiZC9rYiE64h1cCxQgwnG+yHeoPIXOZF50e7HvDGMEVl1POtp1a+YVdWQhf9ifmPRyrSJLnfddOSIZvDUwmIGQaA4heF50d4wAugjdZLFSLWn2XMuvGnlYT3G/NLOfCzPVPAiOrT10+/pBWavAokVtFa7ZcQaWxbKpgvz7jBQFpB0L1WLErHD2S26ZkVHzTofwJzl+6zaJNc9WYf2YvQIfzQ804Zu+GHRLSbaEbrInDOo8lCOP8C5xOfDarObrrVqbJSD3dNYvZ3VbBp5PCzykT6ErwJPMTBH2ylTX4eulqJMhrskRqEgeXJBnSy3VecXWdPReOyYSZ3R7mVH8MnV6nwFUU/anIfxF9QnaSECB7C8tHxHOxCIQQVqtWTzNST6KAkZHo9O70jP9Z1N739GSDE6jrfC X-Forefront-Antispam-Report: CIP:63.35.35.123;CTRY:IE;LANG:en;SCL:1;SRV:;IPV:CAL;SFV:NSPM;H:64aa7808-outbound-1.mta.getcheckrecipient.com;PTR:ec2-63-35-35-123.eu-west-1.compute.amazonaws.com;CAT:NONE;SFS:(13230028)(4636009)(376002)(396003)(136003)(39860400002)(346002)(451199021)(36840700001)(40470700004)(46966006)(40460700003)(30864003)(2906002)(6486002)(82310400005)(82740400003)(356005)(6666004)(81166007)(83380400001)(2616005)(336012)(6512007)(47076005)(53546011)(186003)(26005)(966005)(36860700001)(41300700001)(86362001)(6506007)(478600001)(36756003)(316002)(40480700001)(70586007)(70206006)(4326008)(8936002)(8676002)(6862004)(84970400001)(5660300002)(44832011)(2004002)(15398625002)(579004)(559001);DIR:OUT;SFP:1101; X-OriginatorOrg: arm.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 23 Jun 2023 11:52:21.9364 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: 32d2f44b-09de-4ef2-ad3a-08db73e04dde X-MS-Exchange-CrossTenant-Id: f34e5979-57d9-4aaa-ad4d-b122a662184d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=f34e5979-57d9-4aaa-ad4d-b122a662184d;Ip=[63.35.35.123];Helo=[64aa7808-outbound-1.mta.getcheckrecipient.com] X-MS-Exchange-CrossTenant-AuthSource: DBAEUR03FT060.eop-EUR03.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: AS8PR08MB9930 X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,FORGED_SPF_HELO,GIT_PATCH_0,KAM_DMARC_NONE,KAM_SHORT,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_PASS,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE,UNPARSEABLE_RELAY autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: Hi David, On 31/05/2023 14:06, David Malcolm via Gcc-patches wrote: > Existing text output in GCC has to be implemented by writing > sequentially to a pretty_printer instance. This makes it > hard to implement some kinds of diagnostic output (see e.g. > diagnostic-show-locus.cc). >=20 > This patch adds more flexible ways of creating text output: > - a canvas class, which can be "painted" to via random-access (rather > that sequentially) > - a table class for 2D grid layout, supporting items that span > multiple rows/columns > - a widget class for organizing diagrams hierarchically. >=20 > The patch also expands GCC's diagnostics subsystem so that diagnostics > can have "text art" diagrams - think ASCII art, but potentially > including some Unicode characters, such as box-drawing chars. >=20 > The new code is in a new "gcc/text-art" subdirectory and "text_art" > namespace. It looks like this patch breaks bootstrap on Darwin. I tried a bootstrap on x86_64-apple-darwin and got errors building selftest-run-tests.cc: In file included from /Users/alecop01/toolchain/src/gcc/gcc/selftest-run-te= sts.cc:31: In file included from /Users/alecop01/toolchain/src/gcc/gcc/text-art/selfte= sts.h:25: In file included from /Users/alecop01/toolchain/src/gcc/gcc/text-art/types.= h:26: In file included from /Applications/Xcode.app/Contents/Developer/Platforms/= MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/vector:276: In file included from /Applications/Xcode.app/Contents/Developer/Platforms/= MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/__bit_referenc= e:15: In file included from /Applications/Xcode.app/Contents/Developer/Platforms/= MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/algorithm:653: In file included from /Applications/Xcode.app/Contents/Developer/Platforms/= MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/v1/memory:670: /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Develo= per/SDKs/MacOSX.sdk/usr/include/c++/v1/typeinfo:377:5: error: no member nam= ed 'fancy_abort' in namespace 'std::__1'; did you mean simply 'fancy_abort'= ? _VSTD::abort(); ^~~~~~~ /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Develo= per/SDKs/MacOSX.sdk/usr/include/c++/v1/__config:856:15: note: expanded from= macro '_VSTD' #define _VSTD std::_LIBCPP_ABI_NAMESPACE ^ /Users/alecop01/toolchain/src/gcc/gcc/system.h:811:13: note: 'fancy_abort' = declared here extern void fancy_abort (const char *, int, const char *) ^ Please could you take a look? Thanks, Alex >=20 > The patch adds a new "-fdiagnostics-text-art-charset=3DVAL" option, with > values: > - "none": don't emit diagrams (added to -fdiagnostics-plain-output) > - "ascii": use pure ASCII in diagrams > - "unicode": allow for conservative use of unicode drawing characters > (such as box-drawing characters). > - "emoji" (the default): as "unicode", but potentially allow for > conservative use of emoji in the output (such as U+26A0 WARNING SIGN). > I made it possible to disable emoji separately from unicode as I believe > there's a generation gap in acceptance of these characters (some older > programmers have a visceral reaction against them, whereas younger > programmers may have no problem with them). >=20 > Diagrams are emitted to stderr by default. With SARIF output they are > captured as a location in "relatedLocations", with the diagram as a > code block in Markdown within a "markdown" property of a message. >=20 > This patch doesn't add any such diagram usage to GCC, saving that for > followups, apart from adding a plugin to the test suite to exercise the > functionality. >=20 > One issue is that in various places in the seltftests I've embedded > UTF-8 encoded characters in the source code. Is that acceptable, or > do I need to e.g. move those tests to DejaGnu? >=20 > contrib/ChangeLog: > * unicode/gen-box-drawing-chars.py: New file. > * unicode/gen-combining-chars.py: New file. > * unicode/gen-printable-chars.py: New file. >=20 > gcc/ChangeLog: > * Makefile.in (OBJS-libcommon): Add text-art/box-drawing.o, > text-art/canvas.o, text-art/ruler.o, text-art/selftests.o, > text-art/style.o, text-art/styled-string.o, text-art/table.o, > text-art/theme.o, and text-art/widget.o. > * color-macros.h (COLOR_FG_BRIGHT_BLACK): New. > (COLOR_FG_BRIGHT_RED): New. > (COLOR_FG_BRIGHT_GREEN): New. > (COLOR_FG_BRIGHT_YELLOW): New. > (COLOR_FG_BRIGHT_BLUE): New. > (COLOR_FG_BRIGHT_MAGENTA): New. > (COLOR_FG_BRIGHT_CYAN): New. > (COLOR_FG_BRIGHT_WHITE): New. > (COLOR_BG_BRIGHT_BLACK): New. > (COLOR_BG_BRIGHT_RED): New. > (COLOR_BG_BRIGHT_GREEN): New. > (COLOR_BG_BRIGHT_YELLOW): New. > (COLOR_BG_BRIGHT_BLUE): New. > (COLOR_BG_BRIGHT_MAGENTA): New. > (COLOR_BG_BRIGHT_CYAN): New. > (COLOR_BG_BRIGHT_WHITE): New. > * common.opt (fdiagnostics-text-art-charset=3D): New option. > (diagnostic-text-art.h): New SourceInclude. > (diagnostic_text_art_charset) New Enum and EnumValues. > * configure: Regenerate. > * configure.ac (gccdepdir): Add text-art to loop. > * diagnostic-diagram.h: New file. > * diagnostic-format-json.cc (json_emit_diagram): New. > (diagnostic_output_format_init_json): Wire it up to > context->m_diagrams.m_emission_cb. > * diagnostic-format-sarif.cc: Include "diagnostic-diagram.h" and > "text-art/canvas.h". > (sarif_result::on_nested_diagnostic): Move code to... > (sarif_result::add_related_location): ...this new function. > (sarif_result::on_diagram): New. > (sarif_builder::emit_diagram): New. > (sarif_builder::make_message_object_for_diagram): New. > (sarif_emit_diagram): New. > (diagnostic_output_format_init_sarif): Set > context->m_diagrams.m_emission_cb to sarif_emit_diagram. > * diagnostic-text-art.h: New file. > * diagnostic.cc: Include "diagnostic-text-art.h", > "diagnostic-diagram.h", and "text-art/theme.h". > (diagnostic_initialize): Initialize context->m_diagrams and > call diagnostics_text_art_charset_init. > (diagnostic_finish): Clean up context->m_diagrams.m_theme. > (diagnostic_emit_diagram): New. > (diagnostics_text_art_charset_init): New. > * diagnostic.h (text_art::theme): New forward decl. > (class diagnostic_diagram): Likewise. > (diagnostic_context::m_diagrams): New field. > (diagnostic_emit_diagram): New decl. > * doc/invoke.texi (Diagnostic Message Formatting Options): Add > -fdiagnostics-text-art-charset=3D. > (-fdiagnostics-plain-output): Add > -fdiagnostics-text-art-charset=3Dnone. > * gcc.cc: Include "diagnostic-text-art.h". > (driver_handle_option): Handle OPT_fdiagnostics_text_art_charset_. > * opts-common.cc (decode_cmdline_options_to_array): Add > "-fdiagnostics-text-art-charset=3Dnone" to expanded_args for > -fdiagnostics-plain-output. > * opts.cc: Include "diagnostic-text-art.h". > (common_handle_option): Handle OPT_fdiagnostics_text_art_charset_. > * pretty-print.cc (pp_unicode_character): New. > * pretty-print.h (pp_unicode_character): New decl. > * selftest-run-tests.cc: Include "text-art/selftests.h". > (selftest::run_tests): Call text_art_tests. > * text-art/box-drawing-chars.inc: New file, generated by > contrib/unicode/gen-box-drawing-chars.py. > * text-art/box-drawing.cc: New file. > * text-art/box-drawing.h: New file. > * text-art/canvas.cc: New file. > * text-art/canvas.h: New file. > * text-art/ruler.cc: New file. > * text-art/ruler.h: New file. > * text-art/selftests.cc: New file. > * text-art/selftests.h: New file. > * text-art/style.cc: New file. > * text-art/styled-string.cc: New file. > * text-art/table.cc: New file. > * text-art/table.h: New file. > * text-art/theme.cc: New file. > * text-art/theme.h: New file. > * text-art/types.h: New file. > * text-art/widget.cc: New file. > * text-art/widget.h: New file. >=20 > gcc/testsuite/ChangeLog: > * gcc.dg/plugin/diagnostic-test-text-art-ascii-bw.c: New test. > * gcc.dg/plugin/diagnostic-test-text-art-ascii-color.c: New test. > * gcc.dg/plugin/diagnostic-test-text-art-none.c: New test. > * gcc.dg/plugin/diagnostic-test-text-art-unicode-bw.c: New test. > * gcc.dg/plugin/diagnostic-test-text-art-unicode-color.c: New test. > * gcc.dg/plugin/diagnostic_plugin_test_text_art.c: New test plugin. > * gcc.dg/plugin/plugin.exp (plugin_test_list): Add them. >=20 > libcpp/ChangeLog: > * charset.cc (get_cppchar_property): New function template, based > on... > (cpp_wcwidth): ...this function. Rework to use the above. > Include "combining-chars.inc". > (cpp_is_combining_char): New function > Include "printable-chars.inc". > (cpp_is_printable_char): New function > * combining-chars.inc: New file, generated by > contrib/unicode/gen-combining-chars.py. > * include/cpplib.h (cpp_is_combining_char): New function decl. > (cpp_is_printable_char): New function decl. > * printable-chars.inc: New file, generated by > contrib/unicode/gen-printable-chars.py. > --- > contrib/unicode/gen-box-drawing-chars.py | 94 ++ > contrib/unicode/gen-combining-chars.py | 75 + > contrib/unicode/gen-printable-chars.py | 77 + > gcc/Makefile.in | 11 +- > gcc/color-macros.h | 16 + > gcc/common.opt | 23 + > gcc/configure | 2 +- > gcc/configure.ac | 2 +- > gcc/diagnostic-diagram.h | 51 + > gcc/diagnostic-format-json.cc | 10 + > gcc/diagnostic-format-sarif.cc | 106 +- > gcc/diagnostic-text-art.h | 49 + > gcc/diagnostic.cc | 72 + > gcc/diagnostic.h | 21 + > gcc/doc/invoke.texi | 25 +- > gcc/gcc.cc | 6 + > gcc/opts-common.cc | 1 + > gcc/opts.cc | 6 + > gcc/pretty-print.cc | 29 + > gcc/pretty-print.h | 1 + > gcc/selftest-run-tests.cc | 3 + > .../diagnostic-test-text-art-ascii-bw.c | 57 + > .../diagnostic-test-text-art-ascii-color.c | 58 + > .../plugin/diagnostic-test-text-art-none.c | 5 + > .../diagnostic-test-text-art-unicode-bw.c | 58 + > .../diagnostic-test-text-art-unicode-color.c | 59 + > .../plugin/diagnostic_plugin_test_text_art.c | 257 ++++ > gcc/testsuite/gcc.dg/plugin/plugin.exp | 6 + > gcc/text-art/box-drawing-chars.inc | 18 + > gcc/text-art/box-drawing.cc | 72 + > gcc/text-art/box-drawing.h | 32 + > gcc/text-art/canvas.cc | 437 ++++++ > gcc/text-art/canvas.h | 74 + > gcc/text-art/ruler.cc | 723 ++++++++++ > gcc/text-art/ruler.h | 125 ++ > gcc/text-art/selftests.cc | 77 + > gcc/text-art/selftests.h | 60 + > gcc/text-art/style.cc | 632 ++++++++ > gcc/text-art/styled-string.cc | 1107 ++++++++++++++ > gcc/text-art/table.cc | 1272 +++++++++++++++++ > gcc/text-art/table.h | 262 ++++ > gcc/text-art/theme.cc | 183 +++ > gcc/text-art/theme.h | 123 ++ > gcc/text-art/types.h | 504 +++++++ > gcc/text-art/widget.cc | 275 ++++ > gcc/text-art/widget.h | 246 ++++ > libcpp/charset.cc | 89 +- > libcpp/combining-chars.inc | 68 + > libcpp/include/cpplib.h | 3 + > libcpp/printable-chars.inc | 231 +++ > 50 files changed, 7760 insertions(+), 33 deletions(-) > create mode 100755 contrib/unicode/gen-box-drawing-chars.py > create mode 100755 contrib/unicode/gen-combining-chars.py > create mode 100755 contrib/unicode/gen-printable-chars.py > create mode 100644 gcc/diagnostic-diagram.h > create mode 100644 gcc/diagnostic-text-art.h > create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-= ascii-bw.c > create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-= ascii-color.c > create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-= none.c > create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-= unicode-bw.c > create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-= unicode-color.c > create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_te= xt_art.c > create mode 100644 gcc/text-art/box-drawing-chars.inc > create mode 100644 gcc/text-art/box-drawing.cc > create mode 100644 gcc/text-art/box-drawing.h > create mode 100644 gcc/text-art/canvas.cc > create mode 100644 gcc/text-art/canvas.h > create mode 100644 gcc/text-art/ruler.cc > create mode 100644 gcc/text-art/ruler.h > create mode 100644 gcc/text-art/selftests.cc > create mode 100644 gcc/text-art/selftests.h > create mode 100644 gcc/text-art/style.cc > create mode 100644 gcc/text-art/styled-string.cc > create mode 100644 gcc/text-art/table.cc > create mode 100644 gcc/text-art/table.h > create mode 100644 gcc/text-art/theme.cc > create mode 100644 gcc/text-art/theme.h > create mode 100644 gcc/text-art/types.h > create mode 100644 gcc/text-art/widget.cc > create mode 100644 gcc/text-art/widget.h > create mode 100644 libcpp/combining-chars.inc > create mode 100644 libcpp/printable-chars.inc >=20 > diff --git a/contrib/unicode/gen-box-drawing-chars.py b/contrib/unicode/g= en-box-drawing-chars.py > new file mode 100755 > index 00000000000..9a55266ab84 > --- /dev/null > +++ b/contrib/unicode/gen-box-drawing-chars.py > @@ -0,0 +1,94 @@ > +#!/usr/bin/env python3 > +# > +# Script to generate gcc/text-art/box-drawing-chars.inc > +# > +# This file is part of GCC. > +# > +# GCC is free software; you can redistribute it and/or modify it under > +# the terms of the GNU General Public License as published by the Free > +# Software Foundation; either version 3, or (at your option) any later > +# version. > +# > +# GCC is distributed in the hope that it will be useful, but WITHOUT ANY > +# WARRANTY; without even the implied warranty of MERCHANTABILITY or > +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +# for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with GCC; see the file COPYING3. If not see > +# . */ > + > +import unicodedata > + > +def get_box_drawing_char_name(up: bool, > + down: bool, > + left: bool, > + right: bool) -> str: > + if 0: > + print(f'{locals()=3D}') > + if up and down: > + vertical =3D True > + up =3D False > + down =3D False > + else: > + vertical =3D False > + > + if left and right: > + horizontal =3D True > + left =3D False > + right =3D False > + else: > + horizontal =3D False > + > + weights =3D [] > + heavy =3D [] > + light =3D [] > + dirs =3D [] > + for dir_name in ('up', 'down', 'vertical', 'left', 'right', 'horizon= tal'): > + val =3D locals()[dir_name] > + if val: > + dirs.append(dir_name.upper()) > + > + if not dirs: > + return 'SPACE' > + > + name =3D 'BOX DRAWINGS' > + #print(f'{light=3D} {heavy=3D}') > + > + if 0: > + print(dirs) > + > + def weights_frag(weight: str, dirs: list, prefix: bool): > + """ > + Generate a fragment where all directions share the same weight, = e.g.: > + 'HEAVY HORIZONTAL' > + 'DOWN LIGHT' > + 'LEFT DOWN HEAVY' > + 'HEAVY DOWN AND RIGHT' > + """ > + assert len(dirs) >=3D 1 > + assert len(dirs) <=3D 2 > + if prefix: > + return f' {weight} ' + (' AND '.join(dirs)) > + else: > + return ' ' + (' '.join(dirs)) + f' {weight}' > + > + assert(len(dirs) >=3D 1 and len(dirs) <=3D 2) > + name +=3D weights_frag('LIGHT', dirs, True) > + > + return name > + > +print('/* Generated by contrib/unicode/gen-box-drawing-chars.py. */') > +print() > +for i in range(16): > + up =3D (i & 8) > + down =3D (i & 4) > + left =3D (i & 2) > + right =3D (i & 1) > + name =3D get_box_drawing_char_name(up, down, left, right) > + if i < 15: > + trailing_comma =3D ',' > + else: > + trailing_comma =3D ' ' > + unichar =3D unicodedata.lookup(name) > + print(f'0x{ord(unichar):04X}{trailing_comma} /* "{unichar}": U+{ord(= unichar):04X}: {name} */') > diff --git a/contrib/unicode/gen-combining-chars.py b/contrib/unicode/gen= -combining-chars.py > new file mode 100755 > index 00000000000..fb5ef50ba4c > --- /dev/null > +++ b/contrib/unicode/gen-combining-chars.py > @@ -0,0 +1,75 @@ > +#!/usr/bin/env python3 > +# > +# Script to generate libcpp/combining-chars.inc > +# > +# This file is part of GCC. > +# > +# GCC is free software; you can redistribute it and/or modify it under > +# the terms of the GNU General Public License as published by the Free > +# Software Foundation; either version 3, or (at your option) any later > +# version. > +# > +# GCC is distributed in the hope that it will be useful, but WITHOUT ANY > +# WARRANTY; without even the implied warranty of MERCHANTABILITY or > +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +# for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with GCC; see the file COPYING3. If not see > +# . */ > + > +from pprint import pprint > +import unicodedata > + > +def is_combining_char(code_point) -> bool: > + return unicodedata.combining(chr(code_point)) !=3D 0 > + > +class Range: > + def __init__(self, start, end, value): > + self.start =3D start > + self.end =3D end > + self.value =3D value > + > + def __repr__(self): > + return f'Range({self.start:x}, {self.end:x}, {self.value})' > + > +def make_ranges(value_callback): > + ranges =3D [] > + for code_point in range(0x10FFFF): > + value =3D is_combining_char(code_point) > + if 0: > + print(f'{code_point=3D:x} {value=3D}') > + if ranges and ranges[-1].value =3D=3D value: > + # Extend current range > + ranges[-1].end =3D code_point > + else: > + # Start a new range > + ranges.append(Range(code_point, code_point, value)) > + return ranges > + > +ranges =3D make_ranges(is_combining_char) > +if 0: > + pprint(ranges) > + > +print(f"/* Generated by contrib/unicode/gen-combining-chars.py") > +print(f" using version {unicodedata.unidata_version}" > + " of the Unicode standard. */") > +print("\nstatic const cppchar_t combining_range_ends[] =3D {", end=3D"") > +for i, r in enumerate(ranges): > + if i % 8: > + print(" ", end=3D"") > + else: > + print("\n ", end=3D"") > + print("0x%x," % r.end, end=3D"") > +print("\n};\n") > +print("static const bool is_combining[] =3D {", end=3D"") > +for i, r in enumerate(ranges): > + if i % 24: > + print(" ", end=3D"") > + else: > + print("\n ", end=3D"") > + if r.value: > + print("1,", end=3D"") > + else: > + print("0,", end=3D"") > +print("\n};") > diff --git a/contrib/unicode/gen-printable-chars.py b/contrib/unicode/gen= -printable-chars.py > new file mode 100755 > index 00000000000..7684c086638 > --- /dev/null > +++ b/contrib/unicode/gen-printable-chars.py > @@ -0,0 +1,77 @@ > +#!/usr/bin/env python3 > +# > +# Script to generate libcpp/printable-chars.inc > +# > +# This file is part of GCC. > +# > +# GCC is free software; you can redistribute it and/or modify it under > +# the terms of the GNU General Public License as published by the Free > +# Software Foundation; either version 3, or (at your option) any later > +# version. > +# > +# GCC is distributed in the hope that it will be useful, but WITHOUT ANY > +# WARRANTY; without even the implied warranty of MERCHANTABILITY or > +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +# for more details. > +# > +# You should have received a copy of the GNU General Public License > +# along with GCC; see the file COPYING3. If not see > +# . */ > + > +from pprint import pprint > +import unicodedata > + > +def is_printable_char(code_point) -> bool: > + category =3D unicodedata.category(chr(code_point)) > + # "Cc" is "control" and "Cf" is "format" > + return category[0] !=3D 'C' > + > +class Range: > + def __init__(self, start, end, value): > + self.start =3D start > + self.end =3D end > + self.value =3D value > + > + def __repr__(self): > + return f'Range({self.start:x}, {self.end:x}, {self.value})' > + > +def make_ranges(value_callback): > + ranges =3D [] > + for code_point in range(0x10FFFF): > + value =3D is_printable_char(code_point) > + if 0: > + print(f'{code_point=3D:x} {value=3D}') > + if ranges and ranges[-1].value =3D=3D value: > + # Extend current range > + ranges[-1].end =3D code_point > + else: > + # Start a new range > + ranges.append(Range(code_point, code_point, value)) > + return ranges > + > +ranges =3D make_ranges(is_printable_char) > +if 0: > + pprint(ranges) > + > +print(f"/* Generated by contrib/unicode/gen-printable-chars.py") > +print(f" using version {unicodedata.unidata_version}" > + " of the Unicode standard. */") > +print("\nstatic const cppchar_t printable_range_ends[] =3D {", end=3D"") > +for i, r in enumerate(ranges): > + if i % 8: > + print(" ", end=3D"") > + else: > + print("\n ", end=3D"") > + print("0x%x," % r.end, end=3D"") > +print("\n};\n") > +print("static const bool is_printable[] =3D {", end=3D"") > +for i, r in enumerate(ranges): > + if i % 24: > + print(" ", end=3D"") > + else: > + print("\n ", end=3D"") > + if r.value: > + print("1,", end=3D"") > + else: > + print("0,", end=3D"") > +print("\n};") > diff --git a/gcc/Makefile.in b/gcc/Makefile.in > index 1d39e6dd3f8..c1e7257ed24 100644 > --- a/gcc/Makefile.in > +++ b/gcc/Makefile.in > @@ -1781,7 +1781,16 @@ OBJS-libcommon =3D diagnostic-spec.o diagnostic.o = diagnostic-color.o \ > json.o \ > sbitmap.o \ > vec.o input.o hash-table.o ggc-none.o memory-block.o \ > - selftest.o selftest-diagnostic.o sort.o > + selftest.o selftest-diagnostic.o sort.o \ > + text-art/box-drawing.o \ > + text-art/canvas.o \ > + text-art/ruler.o \ > + text-art/selftests.o \ > + text-art/style.o \ > + text-art/styled-string.o \ > + text-art/table.o \ > + text-art/theme.o \ > + text-art/widget.o > =20 > # Objects in libcommon-target.a, used by drivers and by the core > # compiler and containing target-dependent code. > diff --git a/gcc/color-macros.h b/gcc/color-macros.h > index fcd79d09c01..9688f92110a 100644 > --- a/gcc/color-macros.h > +++ b/gcc/color-macros.h > @@ -92,6 +92,14 @@ along with GCC; see the file COPYING3. If not see > #define COLOR_FG_MAGENTA "35" > #define COLOR_FG_CYAN "36" > #define COLOR_FG_WHITE "37" > +#define COLOR_FG_BRIGHT_BLACK "90" > +#define COLOR_FG_BRIGHT_RED "91" > +#define COLOR_FG_BRIGHT_GREEN "92" > +#define COLOR_FG_BRIGHT_YELLOW "93" > +#define COLOR_FG_BRIGHT_BLUE "94" > +#define COLOR_FG_BRIGHT_MAGENTA "95" > +#define COLOR_FG_BRIGHT_CYAN "96" > +#define COLOR_FG_BRIGHT_WHITE "97" > #define COLOR_BG_BLACK "40" > #define COLOR_BG_RED "41" > #define COLOR_BG_GREEN "42" > @@ -100,6 +108,14 @@ along with GCC; see the file COPYING3. If not see > #define COLOR_BG_MAGENTA "45" > #define COLOR_BG_CYAN "46" > #define COLOR_BG_WHITE "47" > +#define COLOR_BG_BRIGHT_BLACK "100" > +#define COLOR_BG_BRIGHT_RED "101" > +#define COLOR_BG_BRIGHT_GREEN "102" > +#define COLOR_BG_BRIGHT_YELLOW "103" > +#define COLOR_BG_BRIGHT_BLUE "104" > +#define COLOR_BG_BRIGHT_MAGENTA "105" > +#define COLOR_BG_BRIGHT_CYAN "106" > +#define COLOR_BG_BRIGHT_WHITE "107" > #define SGR_START "\33[" > #define SGR_END "m\33[K" > #define SGR_SEQ(str) SGR_START str SGR_END > diff --git a/gcc/common.opt b/gcc/common.opt > index a28ca13385a..b3c82b8607c 100644 > --- a/gcc/common.opt > +++ b/gcc/common.opt > @@ -1502,6 +1502,29 @@ fdiagnostics-show-path-depths > Common Var(flag_diagnostics_show_path_depths) Init(0) > Show stack depths of events in paths. > =20 > +fdiagnostics-text-art-charset=3D > +Driver Common Joined RejectNegative Var(flag_diagnostics_text_art_charse= t) Enum(diagnostic_text_art_charset) Init(DIAGNOSTICS_TEXT_ART_CHARSET_EMOJ= I) > +-fdiagnostics-text-art-charset=3D[none|ascii|unicode|emoji] Determine wh= ich characters to use in text arg diagrams. > + > +; Required for these enum values. > +SourceInclude > +diagnostic-text-art.h > + > +Enum > +Name(diagnostic_text_art_charset) Type(int) > + > +EnumValue > +Enum(diagnostic_text_art_charset) String(none) Value(DIAGNOSTICS_TEXT_AR= T_CHARSET_NONE) > + > +EnumValue > +Enum(diagnostic_text_art_charset) String(ascii) Value(DIAGNOSTICS_TEXT_A= RT_CHARSET_ASCII) > + > +EnumValue > +Enum(diagnostic_text_art_charset) String(unicode) Value(DIAGNOSTICS_TEXT= _ART_CHARSET_UNICODE) > + > +EnumValue > +Enum(diagnostic_text_art_charset) String(emoji) Value(DIAGNOSTICS_TEXT_A= RT_CHARSET_EMOJI) > + > fdiagnostics-minimum-margin-width=3D > Common Joined UInteger Var(diagnostics_minimum_margin_width) Init(6) > Set minimum width of left margin of source code when showing source. > diff --git a/gcc/configure b/gcc/configure > index 5f67808b774..e061d2b1949 100755 > --- a/gcc/configure > +++ b/gcc/configure > @@ -33995,7 +33995,7 @@ $as_echo "$as_me: executing $ac_file commands" >&= 6;} > "depdir":C) $SHELL $ac_aux_dir/mkinstalldirs $DEPDIR ;; > "gccdepdir":C) > ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR > - for lang in $subdirs c-family common analyzer rtl-ssa > + for lang in $subdirs c-family common analyzer text-art rtl-ssa > do > ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR > done ;; > diff --git a/gcc/configure.ac b/gcc/configure.ac > index cc8dd9e20bf..350d245c89f 100644 > --- a/gcc/configure.ac > +++ b/gcc/configure.ac > @@ -1384,7 +1384,7 @@ AC_CHECK_HEADERS(ext/hash_map) > ZW_CREATE_DEPDIR > AC_CONFIG_COMMANDS([gccdepdir],[ > ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR > - for lang in $subdirs c-family common analyzer rtl-ssa > + for lang in $subdirs c-family common analyzer text-art rtl-ssa > do > ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR > done], [subdirs=3D"$subdirs" ac_aux_dir=3D$ac_aux_dir DEPDIR=3D$DEPDIR= ]) > diff --git a/gcc/diagnostic-diagram.h b/gcc/diagnostic-diagram.h > new file mode 100644 > index 00000000000..fc923c512ed > --- /dev/null > +++ b/gcc/diagnostic-diagram.h > @@ -0,0 +1,51 @@ > +/* Support for diagrams within diagnostics. > + Copyright (C) 2023 Free Software Foundation, Inc. > + Contributed by David Malcolm > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it under > +the terms of the GNU General Public License as published by the Free > +Software Foundation; either version 3, or (at your option) any later > +version. > + > +GCC is distributed in the hope that it will be useful, but WITHOUT ANY > +WARRANTY; without even the implied warranty of MERCHANTABILITY or > +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +for more details. > + > +You should have received a copy of the GNU General Public License > +along with GCC; see the file COPYING3. If not see > +. */ > + > +#ifndef GCC_DIAGNOSTIC_DIAGRAM_H > +#define GCC_DIAGNOSTIC_DIAGRAM_H > + > +namespace text_art > +{ > + class canvas; > +} // namespace text_art > + > +/* A text art diagram, along with an "alternative text" string > + describing it. */ > + > +class diagnostic_diagram > +{ > + public: > + diagnostic_diagram (const text_art::canvas &canvas, > + const char *alt_text) > + : m_canvas (canvas), > + m_alt_text (alt_text) > + { > + gcc_assert (alt_text); > + } > + > + const text_art::canvas &get_canvas () const { return m_canvas; } > + const char *get_alt_text () const { return m_alt_text; } > + > + private: > + const text_art::canvas &m_canvas; > + const char *const m_alt_text; > +}; > + > +#endif /* ! GCC_DIAGNOSTIC_DIAGRAM_H */ > diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-json.c= c > index 694dddca9e8..539b98b5e74 100644 > --- a/gcc/diagnostic-format-json.cc > +++ b/gcc/diagnostic-format-json.cc > @@ -324,6 +324,15 @@ json_file_final_cb (diagnostic_context *) > free (filename); > } > =20 > +/* Callback for diagnostic_context::m_diagrams.m_emission_cb. */ > + > +static void > +json_emit_diagram (diagnostic_context *, > + const diagnostic_diagram &) > +{ > + /* No-op. */ > +} > + > /* Populate CONTEXT in preparation for JSON output (either to stderr, or > to a file). */ > =20 > @@ -340,6 +349,7 @@ diagnostic_output_format_init_json (diagnostic_contex= t *context) > context->begin_group_cb =3D json_begin_group; > context->end_group_cb =3D json_end_group; > context->print_path =3D NULL; /* handled in json_end_diagnostic. */ > + context->m_diagrams.m_emission_cb =3D json_emit_diagram; > =20 > /* The metadata is handled in JSON format, rather than as text. */ > context->show_cwe =3D false; > diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif= .cc > index fd29ac2ca3b..ac2f5b844e3 100644 > --- a/gcc/diagnostic-format-sarif.cc > +++ b/gcc/diagnostic-format-sarif.cc > @@ -29,6 +29,8 @@ along with GCC; see the file COPYING3. If not see > #include "cpplib.h" > #include "logical-location.h" > #include "diagnostic-client-data-hooks.h" > +#include "diagnostic-diagram.h" > +#include "text-art/canvas.h" > =20 > class sarif_builder; > =20 > @@ -66,8 +68,13 @@ public: > diagnostic_info *diagnostic, > diagnostic_t orig_diag_kind, > sarif_builder *builder); > + void on_diagram (diagnostic_context *context, > + const diagnostic_diagram &diagram, > + sarif_builder *builder); > =20 > private: > + void add_related_location (json::object *location_obj); > + > json::array *m_related_locations_arr; > }; > =20 > @@ -135,7 +142,8 @@ public: > =20 > void end_diagnostic (diagnostic_context *context, diagnostic_info *dia= gnostic, > diagnostic_t orig_diag_kind); > - > + void emit_diagram (diagnostic_context *context, > + const diagnostic_diagram &diagram); > void end_group (); > =20 > void flush_to_file (FILE *outf); > @@ -144,6 +152,9 @@ public: > json::object *make_location_object (const rich_location &rich_loc, > const logical_location *logical_loc); > json::object *make_message_object (const char *msg) const; > + json::object * > + make_message_object_for_diagram (diagnostic_context *context, > + const diagnostic_diagram &diagram); > =20 > private: > sarif_result *make_result_object (diagnostic_context *context, > @@ -261,12 +272,6 @@ sarif_result::on_nested_diagnostic (diagnostic_conte= xt *context, > diagnostic_t /*orig_diag_kind*/, > sarif_builder *builder) > { > - if (!m_related_locations_arr) > - { > - m_related_locations_arr =3D new json::array (); > - set ("relatedLocations", m_related_locations_arr); > - } > - > /* We don't yet generate meaningful logical locations for notes; > sometimes these will related to current_function_decl, but > often they won't. */ > @@ -277,6 +282,39 @@ sarif_result::on_nested_diagnostic (diagnostic_conte= xt *context, > pp_clear_output_area (context->printer); > location_obj->set ("message", message_obj); > =20 > + add_related_location (location_obj); > +} > + > +/* Handle diagrams that occur within a diagnostic group. > + The closest thing in SARIF seems to be to add a location to the > + "releatedLocations" property (SARIF v2.1.0 section 3.27.22), > + and to put the diagram into the "message" property of that location > + (SARIF v2.1.0 section 3.28.5). */ > + > +void > +sarif_result::on_diagram (diagnostic_context *context, > + const diagnostic_diagram &diagram, > + sarif_builder *builder) > +{ > + json::object *location_obj =3D new json::object (); > + json::object *message_obj > + =3D builder->make_message_object_for_diagram (context, diagram); > + location_obj->set ("message", message_obj); > + > + add_related_location (location_obj); > +} > + > +/* Add LOCATION_OBJ to this result's "relatedLocations" array, > + creating it if it doesn't yet exist. */ > + > +void > +sarif_result::add_related_location (json::object *location_obj) > +{ > + if (!m_related_locations_arr) > + { > + m_related_locations_arr =3D new json::array (); > + set ("relatedLocations", m_related_locations_arr); > + } > m_related_locations_arr->append (location_obj); > } > =20 > @@ -348,6 +386,18 @@ sarif_builder::end_diagnostic (diagnostic_context *c= ontext, > } > } > =20 > +/* Implementation of diagnostic_context::m_diagrams.m_emission_cb > + for SARIF output. */ > + > +void > +sarif_builder::emit_diagram (diagnostic_context *context, > + const diagnostic_diagram &diagram) > +{ > + /* We must be within the emission of a top-level diagnostic. */ > + gcc_assert (m_cur_group_result); > + m_cur_group_result->on_diagram (context, diagram, this); > +} > + > /* Implementation of "end_group_cb" for SARIF output. */ > =20 > void > @@ -1115,6 +1165,37 @@ sarif_builder::make_message_object (const char *ms= g) const > return message_obj; > } > =20 > +/* Make a message object (SARIF v2.1.0 section 3.11) for DIAGRAM. > + We emit the diagram as a code block within the Markdown part > + of the message. */ > + > +json::object * > +sarif_builder::make_message_object_for_diagram (diagnostic_context *cont= ext, > + const diagnostic_diagram &diagram) > +{ > + json::object *message_obj =3D new json::object (); > + > + /* "text" property (SARIF v2.1.0 section 3.11.8). */ > + message_obj->set ("text", new json::string (diagram.get_alt_text ())); > + > + char *saved_prefix =3D pp_take_prefix (context->printer); > + pp_set_prefix (context->printer, NULL); > + > + /* "To produce a code block in Markdown, simply indent every line of > + the block by at least 4 spaces or 1 tab." > + Here we use 4 spaces. */ > + diagram.get_canvas ().print_to_pp (context->printer, " "); > + pp_set_prefix (context->printer, saved_prefix); > + > + /* "markdown" property (SARIF v2.1.0 section 3.11.9). */ > + message_obj->set ("markdown", > + new json::string (pp_formatted_text (context->printer))); > + > + pp_clear_output_area (context->printer); > + > + return message_obj; > +} > + > /* Make a multiformatMessageString object (SARIF v2.1.0 section 3.12) > for MSG. */ > =20 > @@ -1630,6 +1711,16 @@ sarif_ice_handler (diagnostic_context *context) > fnotice (stderr, "Internal compiler error:\n"); > } > =20 > +/* Callback for diagnostic_context::m_diagrams.m_emission_cb. */ > + > +static void > +sarif_emit_diagram (diagnostic_context *context, > + const diagnostic_diagram &diagram) > +{ > + gcc_assert (the_builder); > + the_builder->emit_diagram (context, diagram); > +} > + > /* Populate CONTEXT in preparation for SARIF output (either to stderr, o= r > to a file). */ > =20 > @@ -1645,6 +1736,7 @@ diagnostic_output_format_init_sarif (diagnostic_con= text *context) > context->end_group_cb =3D sarif_end_group; > context->print_path =3D NULL; /* handled in sarif_end_diagnostic. */ > context->ice_handler_cb =3D sarif_ice_handler; > + context->m_diagrams.m_emission_cb =3D sarif_emit_diagram; > =20 > /* The metadata is handled in SARIF format, rather than as text. */ > context->show_cwe =3D false; > diff --git a/gcc/diagnostic-text-art.h b/gcc/diagnostic-text-art.h > new file mode 100644 > index 00000000000..a0d8a78f52a > --- /dev/null > +++ b/gcc/diagnostic-text-art.h > @@ -0,0 +1,49 @@ > +/* Copyright (C) 2023 Free Software Foundation, Inc. > + Contributed by David Malcolm . > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it under > +the terms of the GNU General Public License as published by the Free > +Software Foundation; either version 3, or (at your option) any later > +version. > + > +GCC is distributed in the hope that it will be useful, but WITHOUT ANY > +WARRANTY; without even the implied warranty of MERCHANTABILITY or > +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +for more details. > + > +You should have received a copy of the GNU General Public License > +along with GCC; see the file COPYING3. If not see > +. */ > + > +#ifndef GCC_DIAGNOSTIC_TEXT_ART_H > +#define GCC_DIAGNOSTIC_TEXT_ART_H > + > +/* Values for -fdiagnostics-text-art-charset=3D. */ > + > +enum diagnostic_text_art_charset > +{ > + /* No text art diagrams shall be emitted. */ > + DIAGNOSTICS_TEXT_ART_CHARSET_NONE, > + > + /* Use pure ASCII for text art diagrams. */ > + DIAGNOSTICS_TEXT_ART_CHARSET_ASCII, > + > + /* Use ASCII + conservative use of other unicode characters > + in text art diagrams. */ > + DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE, > + > + /* Use Emoji. */ > + DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI > +}; > + > +const enum diagnostic_text_art_charset DIAGNOSTICS_TEXT_ART_CHARSET_DEFA= ULT > + =3D DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI; > + > +extern void > +diagnostics_text_art_charset_init (diagnostic_context *context, > + enum diagnostic_text_art_charset charset); > + > + > +#endif /* ! GCC_DIAGNOSTIC_TEXT_ART_H */ > diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc > index 0f093081161..7c2289f0634 100644 > --- a/gcc/diagnostic.cc > +++ b/gcc/diagnostic.cc > @@ -35,11 +35,14 @@ along with GCC; see the file COPYING3. If not see > #include "diagnostic-metadata.h" > #include "diagnostic-path.h" > #include "diagnostic-client-data-hooks.h" > +#include "diagnostic-text-art.h" > +#include "diagnostic-diagram.h" > #include "edit-context.h" > #include "selftest.h" > #include "selftest-diagnostic.h" > #include "opts.h" > #include "cpplib.h" > +#include "text-art/theme.h" > =20 > #ifdef HAVE_TERMIOS_H > # include > @@ -244,6 +247,10 @@ diagnostic_initialize (diagnostic_context *context, = int n_opts) > context->ice_handler_cb =3D NULL; > context->includes_seen =3D NULL; > context->m_client_data_hooks =3D NULL; > + context->m_diagrams.m_theme =3D NULL; > + context->m_diagrams.m_emission_cb =3D NULL; > + diagnostics_text_art_charset_init (context, > + DIAGNOSTICS_TEXT_ART_CHARSET_DEFAULT); > } > =20 > /* Maybe initialize the color support. We require clients to do this > @@ -320,6 +327,12 @@ diagnostic_finish (diagnostic_context *context) > if (context->final_cb) > context->final_cb (context); > =20 > + if (context->m_diagrams.m_theme) > + { > + delete context->m_diagrams.m_theme; > + context->m_diagrams.m_theme =3D NULL; > + } > + > diagnostic_file_cache_fini (); > =20 > XDELETEVEC (context->classify_diagnostic); > @@ -2174,6 +2187,33 @@ internal_error_no_backtrace (const char *gmsgid, .= ..) > =20 > gcc_unreachable (); > } > + > +/* Emit DIAGRAM to CONTEXT, respecting the output format. */ > + > +void > +diagnostic_emit_diagram (diagnostic_context *context, > + const diagnostic_diagram &diagram) > +{ > + if (context->m_diagrams.m_theme =3D=3D nullptr) > + return; > + > + if (context->m_diagrams.m_emission_cb) > + { > + context->m_diagrams.m_emission_cb (context, diagram); > + return; > + } > + > + /* Default implementation. */ > + char *saved_prefix =3D pp_take_prefix (context->printer); > + pp_set_prefix (context->printer, NULL); > + /* Use a newline before and after and a two-space indent > + to make the diagram stand out a little from the wall of text. */ > + pp_newline (context->printer); > + diagram.get_canvas ().print_to_pp (context->printer, " "); > + pp_newline (context->printer); > + pp_set_prefix (context->printer, saved_prefix); > + pp_flush (context->printer); > +} > =0C > /* Special case error functions. Most are implemented in terms of the > above, or should be. */ > @@ -2316,6 +2356,38 @@ diagnostic_output_format_init (diagnostic_context = *context, > } > } > =20 > +/* Initialize CONTEXT->m_diagrams based on CHARSET. > + Specifically, make a text_art::theme object for m_diagrams.m_theme, > + (or NULL for "no diagrams"). */ > + > +void > +diagnostics_text_art_charset_init (diagnostic_context *context, > + enum diagnostic_text_art_charset charset) > +{ > + delete context->m_diagrams.m_theme; > + switch (charset) > + { > + default: > + gcc_unreachable (); > + > + case DIAGNOSTICS_TEXT_ART_CHARSET_NONE: > + context->m_diagrams.m_theme =3D NULL; > + break; > + > + case DIAGNOSTICS_TEXT_ART_CHARSET_ASCII: > + context->m_diagrams.m_theme =3D new text_art::ascii_theme (); > + break; > + > + case DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE: > + context->m_diagrams.m_theme =3D new text_art::unicode_theme (); > + break; > + > + case DIAGNOSTICS_TEXT_ART_CHARSET_EMOJI: > + context->m_diagrams.m_theme =3D new text_art::emoji_theme (); > + break; > + } > +} > + > /* Implementation of diagnostic_path::num_events vfunc for > simple_diagnostic_path: simply get the number of events in the vec. = */ > =20 > diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h > index 9a51097f146..00b828f230d 100644 > --- a/gcc/diagnostic.h > +++ b/gcc/diagnostic.h > @@ -24,6 +24,11 @@ along with GCC; see the file COPYING3. If not see > #include "pretty-print.h" > #include "diagnostic-core.h" > =20 > +namespace text_art > +{ > + class theme; > +} // namespace text_art > + > /* An enum for controlling what units to use for the column number > when diagnostics are output, used by the -fdiagnostics-column-unit op= tion. > Tabs will be expanded or not according to the value of -ftabstop. Th= e origin > @@ -170,6 +175,7 @@ class edit_context; > namespace json { class value; } > class diagnostic_client_data_hooks; > class logical_location; > +class diagnostic_diagram; > =20 > /* This data structure bundles altogether any information relevant to > the context of a diagnostic message. */ > @@ -417,6 +423,18 @@ struct diagnostic_context > Used by SARIF output to give metadata about the client that's > producing diagnostics. */ > diagnostic_client_data_hooks *m_client_data_hooks; > + > + /* Support for diagrams. */ > + struct > + { > + /* Theme to use when generating diagrams. > + Can be NULL (if text art is disabled). */ > + text_art::theme *m_theme; > + > + /* Callback for emitting diagrams. */ > + void (*m_emission_cb) (diagnostic_context *context, > + const diagnostic_diagram &diagram); > + } m_diagrams; > }; > =20 > inline void > @@ -619,4 +637,7 @@ extern bool warning_enabled_at (location_t, int); > =20 > extern char *get_cwe_url (int cwe); > =20 > +extern void diagnostic_emit_diagram (diagnostic_context *context, > + const diagnostic_diagram &diagram); > + > #endif /* ! GCC_DIAGNOSTIC_H */ > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi > index 898a88ce33e..023a56a647e 100644 > --- a/gcc/doc/invoke.texi > +++ b/gcc/doc/invoke.texi > @@ -316,7 +316,8 @@ Objective-C and Objective-C++ Dialects}. > -fno-show-column > -fdiagnostics-column-unit=3D@r{[}display@r{|}byte@r{]} > -fdiagnostics-column-origin=3D@var{origin} > --fdiagnostics-escape-format=3D@r{[}unicode@r{|}bytes@r{]}} > +-fdiagnostics-escape-format=3D@r{[}unicode@r{|}bytes@r{]} > +-fdiagnostics-text-art-charset=3D@r{[}none@r{|}ascii@r{|}unicode@r{|}emo= ji@r{]}} > =20 > @item Warning Options > @xref{Warning Options,,Options to Request or Suppress Warnings}. > @@ -5066,7 +5067,8 @@ options: > -fno-diagnostics-show-line-numbers > -fdiagnostics-color=3Dnever > -fdiagnostics-urls=3Dnever > --fdiagnostics-path-format=3Dseparate-events} > +-fdiagnostics-path-format=3Dseparate-events > +-fdiagnostics-text-art-charset=3Dnone} > In the future, if GCC changes the default appearance of its diagnostics,= the > corresponding option to disable the new behavior will be added to this l= ist. > =20 > @@ -5592,6 +5594,25 @@ Unicode characters. For the example above, the fo= llowing will be printed: > before<80>after > @end smallexample > =20 > +@opindex fdiagnostics-text-art-charset > +@item -fdiagnostics-text-art-charset=3D@var{CHARSET} > +Some diagnostics can contain ``text art'' diagrams: visualizations creat= ed > +from text, intended to be viewed in a monospaced font. > + > +This option selects which characters should be used for printing such > +diagrams, if any. @var{CHARSET} is @samp{none}, @samp{ascii}, @samp{uni= code}, > +or @samp{emoji}. > + > +The @samp{none} value suppresses the printing of such diagrams. > +The @samp{ascii} value will ensure that such diagrams are pure ASCII > +(``ASCII art''). The @samp{unicode} value will allow for conservative u= se of > +unicode drawing characters (such as box-drawing characters). The @samp{= emoji} > +value further adds the possibility of emoji in the output (such as emitt= ing > +U+26A0 WARNING SIGN followed by U+FE0F VARIATION SELECTOR-16 to select t= he > +emoji variant of the character). > + > +The default is @samp{emoji}. > + > @opindex fdiagnostics-format > @item -fdiagnostics-format=3D@var{FORMAT} > Select a different format for printing diagnostics. > diff --git a/gcc/gcc.cc b/gcc/gcc.cc > index 2ccca00d603..f9f0a7eaad4 100644 > --- a/gcc/gcc.cc > +++ b/gcc/gcc.cc > @@ -46,6 +46,7 @@ compilation is specified by a string called a "spec". = */ > #include "spellcheck.h" > #include "opts-jobserver.h" > #include "common/common-target.h" > +#include "diagnostic-text-art.h" > =20 > =0C > =20 > @@ -4299,6 +4300,11 @@ driver_handle_option (struct gcc_options *opts, > break; > } > =20 > + case OPT_fdiagnostics_text_art_charset_: > + diagnostics_text_art_charset_init (dc, > + (enum diagnostic_text_art_charset)value); > + break; > + > case OPT_Wa_: > { > int prev, j; > diff --git a/gcc/opts-common.cc b/gcc/opts-common.cc > index 23ddcaa3b55..f0c5f483665 100644 > --- a/gcc/opts-common.cc > +++ b/gcc/opts-common.cc > @@ -1068,6 +1068,7 @@ decode_cmdline_options_to_array (unsigned int argc,= const char **argv, > "-fdiagnostics-color=3Dnever", > "-fdiagnostics-urls=3Dnever", > "-fdiagnostics-path-format=3Dseparate-events", > + "-fdiagnostics-text-art-charset=3Dnone" > }; > const int num_expanded =3D ARRAY_SIZE (expanded_args); > opt_array_len +=3D num_expanded - 1; > diff --git a/gcc/opts.cc b/gcc/opts.cc > index 86b94d62b58..3087bdac2c6 100644 > --- a/gcc/opts.cc > +++ b/gcc/opts.cc > @@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see > #include "version.h" > #include "selftest.h" > #include "file-prefix-map.h" > +#include "diagnostic-text-art.h" > =20 > /* In this file all option sets are explicit. */ > #undef OPTION_SET_P > @@ -2887,6 +2888,11 @@ common_handle_option (struct gcc_options *opts, > break; > } > =20 > + case OPT_fdiagnostics_text_art_charset_: > + diagnostics_text_art_charset_init (dc, > + (enum diagnostic_text_art_charset)value); > + break; > + > case OPT_fdiagnostics_parseable_fixits: > dc->extra_output_kind =3D (value > ? EXTRA_DIAGNOSTIC_OUTPUT_fixits_v1 > diff --git a/gcc/pretty-print.cc b/gcc/pretty-print.cc > index 7d294717f50..3d789a23812 100644 > --- a/gcc/pretty-print.cc > +++ b/gcc/pretty-print.cc > @@ -1828,6 +1828,35 @@ pp_string (pretty_printer *pp, const char *str) > pp_maybe_wrap_text (pp, str, str + strlen (str)); > } > =20 > +/* Append code point C to the output area of PRETTY-PRINTER, encoding it > + as UTF-8. */ > + > +void > +pp_unicode_character (pretty_printer *pp, unsigned c) > +{ > + static const uchar masks[6] =3D { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC = }; > + static const uchar limits[6] =3D { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE = }; > + size_t nbytes; > + uchar buf[6], *p =3D &buf[6]; > + > + nbytes =3D 1; > + if (c < 0x80) > + *--p =3D c; > + else > + { > + do > + { > + *--p =3D ((c & 0x3F) | 0x80); > + c >>=3D 6; > + nbytes++; > + } > + while (c >=3D 0x3F || (c & limits[nbytes-1])); > + *--p =3D (c | masks[nbytes-1]); > + } > + > + pp_append_r (pp, (const char *)p, nbytes); > +} > + > /* Append the leading N characters of STRING to the output area of > PRETTY-PRINTER, quoting in hexadecimal non-printable characters. > Setting N =3D -1 is as if N were set to strlen (STRING). The STRING > diff --git a/gcc/pretty-print.h b/gcc/pretty-print.h > index 0230a289df5..369be6e7ba7 100644 > --- a/gcc/pretty-print.h > +++ b/gcc/pretty-print.h > @@ -401,6 +401,7 @@ extern void pp_indent (pretty_printer *); > extern void pp_newline (pretty_printer *); > extern void pp_character (pretty_printer *, int); > extern void pp_string (pretty_printer *, const char *); > +extern void pp_unicode_character (pretty_printer *, unsigned); > =20 > extern void pp_write_text_to_stream (pretty_printer *); > extern void pp_write_text_as_dot_label_to_stream (pretty_printer *, bool= ); > diff --git a/gcc/selftest-run-tests.cc b/gcc/selftest-run-tests.cc > index 915f2129702..e2fc8f84b1b 100644 > --- a/gcc/selftest-run-tests.cc > +++ b/gcc/selftest-run-tests.cc > @@ -28,6 +28,7 @@ along with GCC; see the file COPYING3. If not see > #include "stringpool.h" > #include "attribs.h" > #include "analyzer/analyzer-selftests.h" > +#include "text-art/selftests.h" > =20 > /* This function needed to be split out from selftest.cc as it reference= s > tests from the whole source tree, and so is within > @@ -118,6 +119,8 @@ selftest::run_tests () > /* Run any lang-specific selftests. */ > lang_hooks.run_lang_selftests (); > =20 > + text_art_tests (); > + > /* Run the analyzer selftests (if enabled). */ > ana::selftest::run_analyzer_selftests (); > =20 > diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-ascii-b= w.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-ascii-bw.c > new file mode 100644 > index 00000000000..e4239aab032 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-ascii-bw.c > @@ -0,0 +1,57 @@ > +/* { dg-additional-options "-fdiagnostics-text-art-charset=3Dascii -fdia= gnostics-color=3Dnever" } */ > + > +int non_empty; > + > +/* { dg-begin-multiline-output "" } > + > + A > + B > + C > + > + { dg-end-multiline-output "" } */ > + > +/* { dg-begin-multiline-output "" } > + > + =E2=99=9C =E2=99=9E =E2=99=9D =E2=99=9B =E2=99=9A =E2=99=9D =E2=99=9E = =E2=99=9C > + =E2=99=9F =E2=99=9F =E2=99=9F =E2=99=9F =E2=99=9F =E2=99=9F =E2=99=9F = =E2=99=9F > + =20 > + =20 > + =20 > + =20 > + =E2=99=99 =E2=99=99 =E2=99=99 =E2=99=99 =E2=99=99 =E2=99=99 =E2=99=99 = =E2=99=99 > + =E2=99=96 =E2=99=98 =E2=99=97 =E2=99=95 =E2=99=94 =E2=99=97 =E2=99=98 = =E2=99=96 > + > + { dg-end-multiline-output "" } */ > + > +/* { dg-begin-multiline-output "" } > + > + +--+ > + |=F0=9F=99=82| > + +--+ > + > + { dg-end-multiline-output "" } */ > +/* { dg-begin-multiline-output "" } > + > + +-------+-----+---------------+---------------------+-----------------= ------+-----------------------+ > + |Offsets|Octet| 0 | 1 | 2 = | 3 | > + +-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--= +--+--+--+--+--+--+--+--+--+--+ > + | Octet | Bit |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21= |22|23|24|25|26|27|28|29|30|31| > + +-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--= +--+--+--+--+--+--+--+--+--+--+ > + | 0 | 0 |Version| IHL | DSCP | ECN | = Total Length | > + +-------+-----+-------+-------+---------------+-----+--------+--------= ------------------------------+ > + | 4 | 32 | Identification | Flags | = Fragment Offset | > + +-------+-----+---------------+---------------------+--------+--------= ------------------------------+ > + | 8 | 64 | Time To Live | Protocol | H= eader Checksum | > + +-------+-----+---------------+---------------------+-----------------= ------------------------------+ > + | 12 | 96 | Source IP Address = | > + +-------+-----+-------------------------------------------------------= ------------------------------+ > + | 16 | 128 | Destination IP Address = | > + +-------+-----+-------------------------------------------------------= ------------------------------+ > + | 20 | 160 | = | > + +-------+-----+ = | > + | ... | ... | Options = | > + +-------+-----+ = | > + | 56 | 448 | = | > + +-------+-----+-------------------------------------------------------= ------------------------------+ > + > + { dg-end-multiline-output "" } */ > diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-ascii-c= olor.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-ascii-color.c > new file mode 100644 > index 00000000000..0650428b1ce > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-ascii-color.c > @@ -0,0 +1,58 @@ > +/* { dg-additional-options "-fdiagnostics-text-art-charset=3Dascii -fdia= gnostics-color=3Dalways" } */ > + > +int non_empty; > + > +/* { dg-begin-multiline-output "" } > + > + A > + B > + C > + > + { dg-end-multiline-output "" } */ > + > +/* { dg-begin-multiline-output "" } > + > + =1B[38;2;0;0;0;48;2;240;217;181m=1B[K=E2=99=9C =1B[38;2;0;0;0;48;2;181= ;136;99m=1B[K=E2=99=9E =1B[38;2;0;0;0;48;2;240;217;181m=1B[K=E2=99=9D =1B[3= 8;2;0;0;0;48;2;181;136;99m=1B[K=E2=99=9B =1B[38;2;0;0;0;48;2;240;217;181m= =1B[K=E2=99=9A =1B[38;2;0;0;0;48;2;181;136;99m=1B[K=E2=99=9D =1B[38;2;0;0;0= ;48;2;240;217;181m=1B[K=E2=99=9E =1B[38;2;0;0;0;48;2;181;136;99m=1B[K=E2=99= =9C =1B[m=1B[K > + =1B[38;2;0;0;0;48;2;181;136;99m=1B[K=E2=99=9F =1B[38;2;0;0;0;48;2;240;= 217;181m=1B[K=E2=99=9F =1B[38;2;0;0;0;48;2;181;136;99m=1B[K=E2=99=9F =1B[38= ;2;0;0;0;48;2;240;217;181m=1B[K=E2=99=9F =1B[38;2;0;0;0;48;2;181;136;99m=1B= [K=E2=99=9F =1B[38;2;0;0;0;48;2;240;217;181m=1B[K=E2=99=9F =1B[38;2;0;0;0;4= 8;2;181;136;99m=1B[K=E2=99=9F =1B[38;2;0;0;0;48;2;240;217;181m=1B[K=E2=99= =9F =1B[m=1B[K > + =1B[48;2;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2;240;21= 7;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;= 2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K = =1B[m=1B[K > + =1B[48;2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;2;181;13= 6;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2= ;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K = =1B[m=1B[K > + =1B[48;2;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2;240;21= 7;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;= 2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K = =1B[m=1B[K > + =1B[48;2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;2;181;13= 6;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2= ;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K = =1B[m=1B[K > + =1B[38;2;255;255;255;48;2;240;217;181m=1B[K=E2=99=99 =1B[38;2;255;255;= 255;48;2;181;136;99m=1B[K=E2=99=99 =1B[38;2;255;255;255;48;2;240;217;181m= =1B[K=E2=99=99 =1B[38;2;255;255;255;48;2;181;136;99m=1B[K=E2=99=99 =1B[38;2= ;255;255;255;48;2;240;217;181m=1B[K=E2=99=99 =1B[38;2;255;255;255;48;2;181;= 136;99m=1B[K=E2=99=99 =1B[38;2;255;255;255;48;2;240;217;181m=1B[K=E2=99=99 = =1B[38;2;255;255;255;48;2;181;136;99m=1B[K=E2=99=99 =1B[m=1B[K > + =1B[38;2;255;255;255;48;2;181;136;99m=1B[K=E2=99=96 =1B[38;2;255;255;2= 55;48;2;240;217;181m=1B[K=E2=99=98 =1B[38;2;255;255;255;48;2;181;136;99m=1B= [K=E2=99=97 =1B[38;2;255;255;255;48;2;240;217;181m=1B[K=E2=99=95 =1B[38;2;2= 55;255;255;48;2;181;136;99m=1B[K=E2=99=94 =1B[38;2;255;255;255;48;2;240;217= ;181m=1B[K=E2=99=97 =1B[38;2;255;255;255;48;2;181;136;99m=1B[K=E2=99=98 =1B= [38;2;255;255;255;48;2;240;217;181m=1B[K=E2=99=96 =1B[m=1B[K > + > + { dg-end-multiline-output "" } */ > + > +/* { dg-begin-multiline-output "" } > + > + +--+ > + |=F0=9F=99=82| > + +--+ > + > + { dg-end-multiline-output "" } */ > + > +/* { dg-begin-multiline-output "" } > + > + +-------+-----+---------------+---------------------+-----------------= ------+-----------------------+ > + |Offsets|Octet| 0 | 1 | 2 = | 3 | > + +-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--= +--+--+--+--+--+--+--+--+--+--+ > + | Octet | Bit |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21= |22|23|24|25|26|27|28|29|30|31| > + +-------+-----+-+-+-+-+-+-+-+-+-+-+--+--+--+--+--+--+--+--+--+--+--+--= +--+--+--+--+--+--+--+--+--+--+ > + | 0 | 0 |Version| IHL | DSCP | ECN | = Total Length | > + +-------+-----+-------+-------+---------------+-----+--------+--------= ------------------------------+ > + | 4 | 32 | Identification | Flags | = Fragment Offset | > + +-------+-----+---------------+---------------------+--------+--------= ------------------------------+ > + | 8 | 64 | Time To Live | Protocol | H= eader Checksum | > + +-------+-----+---------------+---------------------+-----------------= ------------------------------+ > + | 12 | 96 | Source IP Address = | > + +-------+-----+-------------------------------------------------------= ------------------------------+ > + | 16 | 128 | Destination IP Address = | > + +-------+-----+-------------------------------------------------------= ------------------------------+ > + | 20 | 160 | = | > + +-------+-----+ = | > + | ... | ... | Options = | > + +-------+-----+ = | > + | 56 | 448 | = | > + +-------+-----+-------------------------------------------------------= ------------------------------+ > + > + { dg-end-multiline-output "" } */ > diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-none.c = b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-none.c > new file mode 100644 > index 00000000000..c8118b46759 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-none.c > @@ -0,0 +1,5 @@ > +/* { dg-additional-options "-fdiagnostics-text-art-charset=3Dnone" } */ > + > +int non_empty; > + > +/* We expect no output. */ > diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-unicode= -bw.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-unicode-bw.c > new file mode 100644 > index 00000000000..c9f5b36571a > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-unicode-bw.c > @@ -0,0 +1,58 @@ > +/* { dg-additional-options "-fdiagnostics-text-art-charset=3Dunicode -fd= iagnostics-color=3Dnever" } */ > + > +int non_empty; > + > +/* { dg-begin-multiline-output "" } > + > + A > + B > + C > + > + { dg-end-multiline-output "" } */ > + > +/* { dg-begin-multiline-output "" } > + > + =E2=99=9C =E2=99=9E =E2=99=9D =E2=99=9B =E2=99=9A =E2=99=9D =E2=99=9E = =E2=99=9C > + =E2=99=9F =E2=99=9F =E2=99=9F =E2=99=9F =E2=99=9F =E2=99=9F =E2=99=9F = =E2=99=9F > + =20 > + =20 > + =20 > + =20 > + =E2=99=99 =E2=99=99 =E2=99=99 =E2=99=99 =E2=99=99 =E2=99=99 =E2=99=99 = =E2=99=99 > + =E2=99=96 =E2=99=98 =E2=99=97 =E2=99=95 =E2=99=94 =E2=99=97 =E2=99=98 = =E2=99=96 > + > + { dg-end-multiline-output "" } */ > + > +/* { dg-begin-multiline-output "" } > + > + =E2=94=8C=E2=94=80=E2=94=80=E2=94=90 > + =E2=94=82=F0=9F=99=82=E2=94=82 > + =E2=94=94=E2=94=80=E2=94=80=E2=94=98 > + > + { dg-end-multiline-output "" } */ > + > +/* { dg-begin-multiline-output "" } > + > + =E2=94=8C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=90 > + =E2=94=82Offsets=E2=94=82Octet=E2=94=82 0 =E2=94=82 = 1 =E2=94=82 2 =E2=94=82 3 = =E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=AC=E2=94=80=E2=94=AC=E2=94=80=E2=94=AC=E2=94=80=E2=94=AC=E2=94=80=E2= =94=AC=E2=94=80=E2=94=AC=E2=94=80=E2=94=AC=E2=94=80=E2=94=BC=E2=94=80=E2=94= =AC=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC= =E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2= =94=AC=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94= =80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80= =E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2= =94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94= =AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80= =E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 Octet =E2=94=82 Bit =E2=94=820=E2=94=821=E2=94=822=E2=94=823= =E2=94=824=E2=94=825=E2=94=826=E2=94=827=E2=94=828=E2=94=829=E2=94=8210=E2= =94=8211=E2=94=8212=E2=94=8213=E2=94=8214=E2=94=8215=E2=94=8216=E2=94=8217= =E2=94=8218=E2=94=8219=E2=94=8220=E2=94=8221=E2=94=8222=E2=94=8223=E2=94=82= 24=E2=94=8225=E2=94=8226=E2=94=8227=E2=94=8228=E2=94=8229=E2=94=8230=E2=94= =8231=E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=B4=E2=94=80=E2=94=B4=E2=94=80=E2=94=B4=E2=94=80=E2=94=BC=E2=94=80=E2= =94=B4=E2=94=80=E2=94=B4=E2=94=80=E2=94=B4=E2=94=80=E2=94=BC=E2=94=80=E2=94= =B4=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4= =E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=80=E2= =94=B4=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94= =80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80= =E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2= =94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94= =B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80= =E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 0 =E2=94=82 0 =E2=94=82Version=E2=94=82 IHL =E2=94= =82 DSCP =E2=94=82 ECN =E2=94=82 Total Length = =E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 4 =E2=94=82 32 =E2=94=82 Identification = =E2=94=82 Flags =E2=94=82 Fragment Offset =E2= =94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 8 =E2=94=82 64 =E2=94=82 Time To Live =E2=94=82 P= rotocol =E2=94=82 Header Checksum =E2= =94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 12 =E2=94=82 96 =E2=94=82 = Source IP Address =E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 16 =E2=94=82 128 =E2=94=82 = Destination IP Address =E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 20 =E2=94=82 160 =E2=94=82 = =E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=A4 = = =E2=94=82 > + =E2=94=82 ... =E2=94=82 ... =E2=94=82 = Options =E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=A4 = = =E2=94=82 > + =E2=94=82 56 =E2=94=82 448 =E2=94=82 = =E2=94=82 > + =E2=94=94=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=B4=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=98 > + > + { dg-end-multiline-output "" } */ > diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-unicode= -color.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-unicode-col= or.c > new file mode 100644 > index 00000000000..f402836f889 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-text-art-unicode-color.= c > @@ -0,0 +1,59 @@ > +/* { dg-additional-options "-fdiagnostics-text-art-charset=3Dunicode -fd= iagnostics-color=3Dalways" } */ > + > +int non_empty; > + > + > +/* { dg-begin-multiline-output "" } > + > + A > + B > + C > + > + { dg-end-multiline-output "" } */ > + > +/* { dg-begin-multiline-output "" } > + > + =1B[38;2;0;0;0;48;2;240;217;181m=1B[K=E2=99=9C =1B[38;2;0;0;0;48;2;181= ;136;99m=1B[K=E2=99=9E =1B[38;2;0;0;0;48;2;240;217;181m=1B[K=E2=99=9D =1B[3= 8;2;0;0;0;48;2;181;136;99m=1B[K=E2=99=9B =1B[38;2;0;0;0;48;2;240;217;181m= =1B[K=E2=99=9A =1B[38;2;0;0;0;48;2;181;136;99m=1B[K=E2=99=9D =1B[38;2;0;0;0= ;48;2;240;217;181m=1B[K=E2=99=9E =1B[38;2;0;0;0;48;2;181;136;99m=1B[K=E2=99= =9C =1B[m=1B[K > + =1B[38;2;0;0;0;48;2;181;136;99m=1B[K=E2=99=9F =1B[38;2;0;0;0;48;2;240;= 217;181m=1B[K=E2=99=9F =1B[38;2;0;0;0;48;2;181;136;99m=1B[K=E2=99=9F =1B[38= ;2;0;0;0;48;2;240;217;181m=1B[K=E2=99=9F =1B[38;2;0;0;0;48;2;181;136;99m=1B= [K=E2=99=9F =1B[38;2;0;0;0;48;2;240;217;181m=1B[K=E2=99=9F =1B[38;2;0;0;0;4= 8;2;181;136;99m=1B[K=E2=99=9F =1B[38;2;0;0;0;48;2;240;217;181m=1B[K=E2=99= =9F =1B[m=1B[K > + =1B[48;2;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2;240;21= 7;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;= 2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K = =1B[m=1B[K > + =1B[48;2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;2;181;13= 6;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2= ;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K = =1B[m=1B[K > + =1B[48;2;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2;240;21= 7;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;= 2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K = =1B[m=1B[K > + =1B[48;2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;2;181;13= 6;99m=1B[K =1B[48;2;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2= ;240;217;181m=1B[K =1B[48;2;181;136;99m=1B[K =1B[48;2;240;217;181m=1B[K = =1B[m=1B[K > + =1B[38;2;255;255;255;48;2;240;217;181m=1B[K=E2=99=99 =1B[38;2;255;255;= 255;48;2;181;136;99m=1B[K=E2=99=99 =1B[38;2;255;255;255;48;2;240;217;181m= =1B[K=E2=99=99 =1B[38;2;255;255;255;48;2;181;136;99m=1B[K=E2=99=99 =1B[38;2= ;255;255;255;48;2;240;217;181m=1B[K=E2=99=99 =1B[38;2;255;255;255;48;2;181;= 136;99m=1B[K=E2=99=99 =1B[38;2;255;255;255;48;2;240;217;181m=1B[K=E2=99=99 = =1B[38;2;255;255;255;48;2;181;136;99m=1B[K=E2=99=99 =1B[m=1B[K > + =1B[38;2;255;255;255;48;2;181;136;99m=1B[K=E2=99=96 =1B[38;2;255;255;2= 55;48;2;240;217;181m=1B[K=E2=99=98 =1B[38;2;255;255;255;48;2;181;136;99m=1B= [K=E2=99=97 =1B[38;2;255;255;255;48;2;240;217;181m=1B[K=E2=99=95 =1B[38;2;2= 55;255;255;48;2;181;136;99m=1B[K=E2=99=94 =1B[38;2;255;255;255;48;2;240;217= ;181m=1B[K=E2=99=97 =1B[38;2;255;255;255;48;2;181;136;99m=1B[K=E2=99=98 =1B= [38;2;255;255;255;48;2;240;217;181m=1B[K=E2=99=96 =1B[m=1B[K > + > + { dg-end-multiline-output "" } */ > + > +/* { dg-begin-multiline-output "" } > + > + =E2=94=8C=E2=94=80=E2=94=80=E2=94=90 > + =E2=94=82=F0=9F=99=82=E2=94=82 > + =E2=94=94=E2=94=80=E2=94=80=E2=94=98 > + > + { dg-end-multiline-output "" } */ > + > +/* { dg-begin-multiline-output "" } > + > + =E2=94=8C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=90 > + =E2=94=82Offsets=E2=94=82Octet=E2=94=82 0 =E2=94=82 = 1 =E2=94=82 2 =E2=94=82 3 = =E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=AC=E2=94=80=E2=94=AC=E2=94=80=E2=94=AC=E2=94=80=E2=94=AC=E2=94=80=E2= =94=AC=E2=94=80=E2=94=AC=E2=94=80=E2=94=AC=E2=94=80=E2=94=BC=E2=94=80=E2=94= =AC=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC= =E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2= =94=AC=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94= =80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80= =E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2= =94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94= =AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80= =E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 Octet =E2=94=82 Bit =E2=94=820=E2=94=821=E2=94=822=E2=94=823= =E2=94=824=E2=94=825=E2=94=826=E2=94=827=E2=94=828=E2=94=829=E2=94=8210=E2= =94=8211=E2=94=8212=E2=94=8213=E2=94=8214=E2=94=8215=E2=94=8216=E2=94=8217= =E2=94=8218=E2=94=8219=E2=94=8220=E2=94=8221=E2=94=8222=E2=94=8223=E2=94=82= 24=E2=94=8225=E2=94=8226=E2=94=8227=E2=94=8228=E2=94=8229=E2=94=8230=E2=94= =8231=E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=B4=E2=94=80=E2=94=B4=E2=94=80=E2=94=B4=E2=94=80=E2=94=BC=E2=94=80=E2= =94=B4=E2=94=80=E2=94=B4=E2=94=80=E2=94=B4=E2=94=80=E2=94=BC=E2=94=80=E2=94= =B4=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4= =E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=80=E2= =94=B4=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94= =80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80= =E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2= =94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94= =B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80= =E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 0 =E2=94=82 0 =E2=94=82Version=E2=94=82 IHL =E2=94= =82 DSCP =E2=94=82 ECN =E2=94=82 Total Length = =E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 4 =E2=94=82 32 =E2=94=82 Identification = =E2=94=82 Flags =E2=94=82 Fragment Offset =E2= =94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 8 =E2=94=82 64 =E2=94=82 Time To Live =E2=94=82 P= rotocol =E2=94=82 Header Checksum =E2= =94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 12 =E2=94=82 96 =E2=94=82 = Source IP Address =E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 16 =E2=94=82 128 =E2=94=82 = Destination IP Address =E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=A4 > + =E2=94=82 20 =E2=94=82 160 =E2=94=82 = =E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=A4 = = =E2=94=82 > + =E2=94=82 ... =E2=94=82 ... =E2=94=82 = Options =E2=94=82 > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=A4 = = =E2=94=82 > + =E2=94=82 56 =E2=94=82 448 =E2=94=82 = =E2=94=82 > + =E2=94=94=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=B4=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=98 > + > + { dg-end-multiline-output "" } */ > diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_text_art.= c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_text_art.c > new file mode 100644 > index 00000000000..27c341b9f2f > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_text_art.c > @@ -0,0 +1,257 @@ > +/* { dg-options "-O" } */ > + > +/* This plugin exercises the text_art code. */ > + > +#include "gcc-plugin.h" > +#include "config.h" > +#include "system.h" > +#include "coretypes.h" > +#include "plugin-version.h" > +#include "diagnostic.h" > +#include "diagnostic-diagram.h" > +#include "text-art/canvas.h" > +#include "text-art/table.h" > + > +int plugin_is_GPL_compatible; > + > +using namespace text_art; > + > +/* Canvas tests. */ > + > +static void > +emit_canvas (const canvas &c, const char *alt_text) > +{ > + diagnostic_diagram diagram (c, alt_text); > + diagnostic_emit_diagram (global_dc, diagram); > +} > + > +static void > +test_abc () > +{ > + style_manager sm; > + canvas c (canvas::size_t (3, 3), sm); > + c.paint (canvas::coord_t (0, 0), styled_unichar ('A')); > + c.paint (canvas::coord_t (1, 1), styled_unichar ('B')); > + c.paint (canvas::coord_t (2, 2), styled_unichar ('C')); > + emit_canvas (c, "test_abc"); > +} > + > +/* Test of procedural art using 24-bit color: chess starting position. = */ > + > +static void > +test_chessboard () > +{ > + /* With the exception of NONE, these are in order of the chess symbols > + in the Unicode Miscellaneous Symbols block. */ > + enum class piece { KING, QUEEN, ROOK, BISHOP, KNIGHT, PAWN, NONE }; > + enum class color { BLACK, WHITE, NONE }; > + > + style_manager sm; > + > + /* We assume double-column chars for the pieces, so allow two canvas > + columns per square. */ > + canvas canvas (canvas::size_t (16, 8), sm); > + > + for (int x =3D 0; x < 8; x++) > + for (int y =3D 0; y < 8; y++) > + { > + enum piece piece_kind; > + enum color piece_color; > + switch (y) > + { > + case 0: > + case 7: > + switch (x) > + { > + default: > + gcc_unreachable (); > + case 0: > + piece_kind =3D piece::ROOK; > + break; > + case 1: > + piece_kind =3D piece::KNIGHT; > + break; > + case 2: > + piece_kind =3D piece::BISHOP; > + break; > + case 3: > + piece_kind =3D piece::QUEEN; > + break; > + case 4: > + piece_kind =3D piece::KING; > + break; > + case 5: > + piece_kind =3D piece::BISHOP; > + break; > + case 6: > + piece_kind =3D piece::KNIGHT; > + break; > + case 7: > + piece_kind =3D piece::ROOK; > + break; > + } > + piece_color =3D (y =3D=3D 0) ? color::BLACK : color::WHITE; > + break; > + case 1: > + case 6: > + piece_kind =3D piece::PAWN; > + piece_color =3D (y =3D=3D 1) ? color::BLACK : color::WHITE; > + break; > + default: > + piece_kind =3D piece::NONE; > + piece_color =3D color::NONE; > + break; > + } > + > + style s; > + const bool white_square =3D (x + y) % 2 =3D=3D 0; > + if (white_square) > + s.m_bg_color =3D style::color (0xf0, 0xd9, 0xb5); > + else > + s.m_bg_color =3D style::color (0xb5, 0x88, 0x63); > + switch (piece_color) > + { > + default: > + gcc_unreachable (); > + case color::WHITE: > + s.m_fg_color =3D style::color (0xff, 0xff, 0xff); > + break; > + case color::BLACK: > + s.m_fg_color =3D style::color (0x00, 0x00, 0x00); > + break; > + case color::NONE: > + break; > + } > + style::id_t style_id =3D sm.get_or_create_id (s); > + > + cppchar_t ch; > + if (piece_kind =3D=3D piece::NONE) > + ch =3D ' '; > + else > + { > + const cppchar_t WHITE_KING =3D 0x2654; > + const cppchar_t BLACK_KING =3D 0x265A; > + cppchar_t base ((piece_color =3D=3D color::WHITE) > + ? WHITE_KING : BLACK_KING); > + ch =3D base + ((int)piece_kind - (int)piece::KING); > + } > + canvas.paint (canvas::coord_t (x * 2, y), > + canvas::cell_t (ch, false, style_id)); > + canvas.paint (canvas::coord_t (x * 2 + 1, y), > + canvas::cell_t (' ', false, style_id)); > + } > + emit_canvas (canvas, "test_chessboard"); > +} > + > +/* Table tests. */ > + > +static void > +emit_table (const table &table, const style_manager &sm, const char *alt= _text) > +{ > + const text_art::theme *theme =3D global_dc->m_diagrams.m_theme; > + if (!theme) > + return; > + canvas c (table.to_canvas (*theme, sm)); > + emit_canvas (c, alt_text); > +} > + > +static void > +test_double_width_chars () > +{ > + style_manager sm; > + table table (table::size_t (1, 1)); > + table.set_cell (table::coord_t (0,0), > + styled_string ((cppchar_t)0x1f642)); > + > + emit_table (table, sm, "test_double_width_chars"); > +} > + > +static void > +test_ipv4_header () > +{ > + style_manager sm; > + table table (table::size_t (34, 10)); > + table.set_cell (table::coord_t (0, 0), styled_string (sm, "Offsets")); > + table.set_cell (table::coord_t (1, 0), styled_string (sm, "Octet")); > + table.set_cell (table::coord_t (0, 1), styled_string (sm, "Octet")); > + for (int octet =3D 0; octet < 4; octet++) > + table.set_cell_span (table::rect_t (table::coord_t (2 + (octet * 8),= 0), > + table::size_t (8, 1)), > + styled_string::from_fmt (sm, nullptr, "%i", octet)); > + table.set_cell (table::coord_t (1, 1), styled_string (sm, "Bit")); > + for (int bit =3D 0; bit < 32; bit++) > + table.set_cell (table::coord_t (bit + 2, 1), > + styled_string::from_fmt (sm, nullptr, "%i", bit)); > + for (int word =3D 0; word < 6; word++) > + { > + table.set_cell (table::coord_t (0, word + 2), > + styled_string::from_fmt (sm, nullptr, "%i", word * 4)); > + table.set_cell (table::coord_t (1, word + 2), > + styled_string::from_fmt (sm, nullptr, "%i", word * 32)); > + } > + > + table.set_cell (table::coord_t (0, 8), styled_string (sm, "...")); > + table.set_cell (table::coord_t (1, 8), styled_string (sm, "...")); > + table.set_cell (table::coord_t (0, 9), styled_string (sm, "56")); > + table.set_cell (table::coord_t (1, 9), styled_string (sm, "448")); > + > +#define SET_BITS(FIRST, LAST, NAME) \ > + do { \ > + const int first =3D (FIRST); \ > + const int last =3D (LAST); \ > + const char *name =3D (NAME); \ > + const int row =3D first / 32; \ > + gcc_assert (last / 32 =3D=3D row); \ > + table::rect_t rect (table::coord_t ((first % 32) + 2, row + 2), \ > + table::size_t (last + 1 - first , 1)); \ > + table.set_cell_span (rect, styled_string (sm, name)); \ > + } while (0) > + > + SET_BITS (0, 3, "Version"); > + SET_BITS (4, 7, "IHL"); > + SET_BITS (8, 13, "DSCP"); > + SET_BITS (14, 15, "ECN"); > + SET_BITS (16, 31, "Total Length"); > + > + SET_BITS (32 + 0, 32 + 15, "Identification"); > + SET_BITS (32 + 16, 32 + 18, "Flags"); > + SET_BITS (32 + 19, 32 + 31, "Fragment Offset"); > + > + SET_BITS (64 + 0, 64 + 7, "Time To Live"); > + SET_BITS (64 + 8, 64 + 15, "Protocol"); > + SET_BITS (64 + 16, 64 + 31, "Header Checksum"); > + > + SET_BITS (96 + 0, 96 + 31, "Source IP Address"); > + SET_BITS (128 + 0, 128 + 31, "Destination IP Address"); > + > + table.set_cell_span(table::rect_t (table::coord_t (2, 7), > + table::size_t (32, 3)), > + styled_string (sm, "Options")); > + > + emit_table (table, sm, "test_ipv4_header"); > +} > + > +static void > +show_diagrams () > +{ > + test_abc (); > + test_chessboard (); > + test_double_width_chars (); > + test_ipv4_header (); > +} > + > +int > +plugin_init (struct plugin_name_args *plugin_info, > + struct plugin_gcc_version *version) > +{ > + const char *plugin_name =3D plugin_info->base_name; > + int argc =3D plugin_info->argc; > + struct plugin_argument *argv =3D plugin_info->argv; > + > + if (!plugin_default_version_check (version, &gcc_version)) > + return 1; > + > + show_diagrams (); > + > + return 0; > +} > diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.d= g/plugin/plugin.exp > index 4d6304cd100..60723a20eda 100644 > --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp > +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp > @@ -114,6 +114,12 @@ set plugin_test_list [list \ > diagnostic-path-format-inline-events-1.c \ > diagnostic-path-format-inline-events-2.c \ > diagnostic-path-format-inline-events-3.c } \ > + { diagnostic_plugin_test_text_art.c \ > + diagnostic-test-text-art-none.c \ > + diagnostic-test-text-art-ascii-bw.c \ > + diagnostic-test-text-art-ascii-color.c \ > + diagnostic-test-text-art-unicode-bw.c \ > + diagnostic-test-text-art-unicode-color.c } \ > { location_overflow_plugin.c \ > location-overflow-test-1.c \ > location-overflow-test-2.c \ > diff --git a/gcc/text-art/box-drawing-chars.inc b/gcc/text-art/box-drawin= g-chars.inc > new file mode 100644 > index 00000000000..a370255d56d > --- /dev/null > +++ b/gcc/text-art/box-drawing-chars.inc > @@ -0,0 +1,18 @@ > +/* Generated by contrib/unicode/gen-box-drawing-chars.py. */ > + > +0x0020, /* " ": U+0020: SPACE */ > +0x2576, /* "=E2=95=B6": U+2576: BOX DRAWINGS LIGHT RIGHT */ > +0x2574, /* "=E2=95=B4": U+2574: BOX DRAWINGS LIGHT LEFT */ > +0x2500, /* "=E2=94=80": U+2500: BOX DRAWINGS LIGHT HORIZONTAL */ > +0x2577, /* "=E2=95=B7": U+2577: BOX DRAWINGS LIGHT DOWN */ > +0x250C, /* "=E2=94=8C": U+250C: BOX DRAWINGS LIGHT DOWN AND RIGHT */ > +0x2510, /* "=E2=94=90": U+2510: BOX DRAWINGS LIGHT DOWN AND LEFT */ > +0x252C, /* "=E2=94=AC": U+252C: BOX DRAWINGS LIGHT DOWN AND HORIZONTAL *= / > +0x2575, /* "=E2=95=B5": U+2575: BOX DRAWINGS LIGHT UP */ > +0x2514, /* "=E2=94=94": U+2514: BOX DRAWINGS LIGHT UP AND RIGHT */ > +0x2518, /* "=E2=94=98": U+2518: BOX DRAWINGS LIGHT UP AND LEFT */ > +0x2534, /* "=E2=94=B4": U+2534: BOX DRAWINGS LIGHT UP AND HORIZONTAL */ > +0x2502, /* "=E2=94=82": U+2502: BOX DRAWINGS LIGHT VERTICAL */ > +0x251C, /* "=E2=94=9C": U+251C: BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ > +0x2524, /* "=E2=94=A4": U+2524: BOX DRAWINGS LIGHT VERTICAL AND LEFT */ > +0x253C /* "=E2=94=BC": U+253C: BOX DRAWINGS LIGHT VERTICAL AND HORIZONT= AL */ > diff --git a/gcc/text-art/box-drawing.cc b/gcc/text-art/box-drawing.cc > new file mode 100644 > index 00000000000..981d0b095cf > --- /dev/null > +++ b/gcc/text-art/box-drawing.cc > @@ -0,0 +1,72 @@ > +/* Procedural lookup of box drawing characters. > + Copyright (C) 2023 Free Software Foundation, Inc. > + Contributed by David Malcolm . > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it under > +the terms of the GNU General Public License as published by the Free > +Software Foundation; either version 3, or (at your option) any later > +version. > + > +GCC is distributed in the hope that it will be useful, but WITHOUT ANY > +WARRANTY; without even the implied warranty of MERCHANTABILITY or > +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +for more details. > + > +You should have received a copy of the GNU General Public License > +along with GCC; see the file COPYING3. If not see > +. */ > + > +#include "config.h" > +#include "system.h" > +#include "coretypes.h" > +#include "text-art/box-drawing.h" > +#include "selftest.h" > +#include "text-art/selftests.h" > + > + > +/* According to > + https://en.wikipedia.org/wiki/Box-drawing_character#Character_code > + "DOS line- and box-drawing characters are not ordered in any programm= atic > + manner, so calculating a particular character shape needs to use a lo= ok-up > + table. " > + Hence this array. */ > +static const cppchar_t box_drawing_chars[] =3D { > +#include "text-art/box-drawing-chars.inc" > +}; > + > +cppchar_t > +text_art::get_box_drawing_char (directions line_dirs) > +{ > + const size_t idx =3D line_dirs.as_index (); > + gcc_assert (idx < 16); > + return box_drawing_chars[idx]; > +} > + > +#if CHECKING_P > + > +namespace selftest { > + > +/* Run all selftests in this file. */ > + > +void > +text_art_box_drawing_cc_tests () > +{ > + ASSERT_EQ (text_art::get_box_drawing_char > + (text_art::directions (false, false, false, false)), > + ' '); > + ASSERT_EQ (text_art::get_box_drawing_char > + (text_art::directions (false, false, true, true)), > + 0x2500); /* BOX DRAWINGS LIGHT HORIZONTAL */ > + ASSERT_EQ (text_art::get_box_drawing_char > + (text_art::directions (true, true, false, false)), > + 0x2502); /* BOX DRAWINGS LIGHT VERTICAL */ > + ASSERT_EQ (text_art::get_box_drawing_char > + (text_art::directions (true, false, true, false)), > + 0x2518); /* BOX DRAWINGS LIGHT UP AND LEFT */ > +} > + > +} // namespace selftest > + > +#endif /* #if CHECKING_P */ > diff --git a/gcc/text-art/box-drawing.h b/gcc/text-art/box-drawing.h > new file mode 100644 > index 00000000000..29f4d9921b3 > --- /dev/null > +++ b/gcc/text-art/box-drawing.h > @@ -0,0 +1,32 @@ > +/* Procedural lookup of box drawing characters. > + Copyright (C) 2023 Free Software Foundation, Inc. > + Contributed by David Malcolm . > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it > +under the terms of the GNU General Public License as published by > +the Free Software Foundation; either version 3, or (at your option) > +any later version. > + > +GCC is distributed in the hope that it will be useful, but > +WITHOUT ANY WARRANTY; without even the implied warranty of > +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > +General Public License for more details. > + > +You should have received a copy of the GNU General Public License > +along with GCC; see the file COPYING3. If not see > +. */ > + > +#ifndef GCC_TEXT_ART_BOX_DRAWING_H > +#define GCC_TEXT_ART_BOX_DRAWING_H > + > +#include "text-art/types.h" > + > +namespace text_art { > + > +extern cppchar_t get_box_drawing_char (directions line_dirs); > + > +} // namespace text_art > + > +#endif /* GCC_TEXT_ART_BOX_DRAWING_H */ > diff --git a/gcc/text-art/canvas.cc b/gcc/text-art/canvas.cc > new file mode 100644 > index 00000000000..f229612c919 > --- /dev/null > +++ b/gcc/text-art/canvas.cc > @@ -0,0 +1,437 @@ > +/* Canvas for random-access procedural text art. > + Copyright (C) 2023 Free Software Foundation, Inc. > + Contributed by David Malcolm . > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it under > +the terms of the GNU General Public License as published by the Free > +Software Foundation; either version 3, or (at your option) any later > +version. > + > +GCC is distributed in the hope that it will be useful, but WITHOUT ANY > +WARRANTY; without even the implied warranty of MERCHANTABILITY or > +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +for more details. > + > +You should have received a copy of the GNU General Public License > +along with GCC; see the file COPYING3. If not see > +. */ > + > +#include "config.h" > +#include "system.h" > +#include "coretypes.h" > +#include "pretty-print.h" > +#include "selftest.h" > +#include "text-art/selftests.h" > +#include "text-art/canvas.h" > + > +using namespace text_art; > + > +canvas::canvas (size_t size, const style_manager &style_mgr) > +: m_cells (size_t (size.w, size.h)), > + m_style_mgr (style_mgr) > +{ > + m_cells.fill (cell_t (' ')); > +} > + > +void > +canvas::paint (coord_t coord, styled_unichar ch) > +{ > + m_cells.set (coord, std::move (ch)); > +} > + > +void > +canvas::paint_text (coord_t coord, const styled_string &text) > +{ > + for (auto ch : text) > + { > + paint (coord, ch); > + if (ch.double_width_p ()) > + coord.x +=3D 2; > + else > + coord.x++; > + } > +} > + > +void > +canvas::fill (rect_t rect, cell_t c) > +{ > + for (int y =3D rect.get_min_y (); y < rect.get_next_y (); y++) > + for (int x =3D rect.get_min_x (); x < rect.get_next_x (); x++) > + paint(coord_t (x, y), c); > +} > + > +void > +canvas::debug_fill () > +{ > + fill (rect_t (coord_t (0, 0), get_size ()), cell_t ('*')); > +} > + > +void > +canvas::print_to_pp (pretty_printer *pp, > + const char *per_line_prefix) const > +{ > + for (int y =3D 0; y < m_cells.get_size ().h; y++) > + { > + style::id_t curr_style_id =3D 0; > + if (per_line_prefix) > + pp_string (pp, per_line_prefix); > + > + pretty_printer line_pp; > + line_pp.show_color =3D pp->show_color; > + line_pp.url_format =3D pp->url_format; > + const int final_x_in_row =3D get_final_x_in_row (y); > + for (int x =3D 0; x <=3D final_x_in_row; x++) > + { > + if (x > 0) > + { > + const cell_t prev_cell =3D m_cells.get (coord_t (x - 1, y)); > + if (prev_cell.double_width_p ()) > + /* This cell is just a placeholder for the > + 2nd column of a double width cell; skip it. */ > + continue; > + } > + const cell_t cell =3D m_cells.get (coord_t (x, y)); > + if (cell.get_style_id () !=3D curr_style_id) > + { > + m_style_mgr.print_any_style_changes (&line_pp, > + curr_style_id, > + cell.get_style_id ()); > + curr_style_id =3D cell.get_style_id (); > + } > + pp_unicode_character (&line_pp, cell.get_code ()); > + if (cell.emoji_variant_p ()) > + /* Append U+FE0F VARIATION SELECTOR-16 to select the emoji > + variation of the char. */ > + pp_unicode_character (&line_pp, 0xFE0F); > + } > + /* Reset the style at the end of each line. */ > + m_style_mgr.print_any_style_changes (&line_pp, curr_style_id, 0); > + > + /* Print from line_pp to pp, stripping trailing whitespace from > + the line. */ > + const char *line_buf =3D pp_formatted_text (&line_pp); > + ::size_t len =3D strlen (line_buf); > + while (len > 0) > + { > + if (line_buf[len - 1] =3D=3D ' ') > + len--; > + else > + break; > + } > + pp_append_text (pp, line_buf, line_buf + len); > + pp_newline (pp); > + } > +} > + > +DEBUG_FUNCTION void > +canvas::debug (bool styled) const > +{ > + pretty_printer pp; > + if (styled) > + { > + pp_show_color (&pp) =3D true; > + pp.url_format =3D determine_url_format (DIAGNOSTICS_URL_AUTO); > + } > + print_to_pp (&pp); > + fprintf (stderr, "%s\n", pp_formatted_text (&pp)); > +} > + > +/* Find right-most non-default cell in this row, > + or -1 if all are default. */ > + > +int > +canvas::get_final_x_in_row (int y) const > +{ > + for (int x =3D m_cells.get_size ().w - 1; x >=3D 0; x--) > + { > + cell_t cell =3D m_cells.get (coord_t (x, y)); > + if (cell.get_code () !=3D ' ' > + || cell.get_style_id () !=3D style::id_plain) > + return x; > + } > + return -1; > +} > + > +#if CHECKING_P > + > +namespace selftest { > + > +static void > +test_blank () > +{ > + style_manager sm; > + canvas c (canvas::size_t (5, 5), sm); > + ASSERT_CANVAS_STREQ (c, false, > + ("\n" > + "\n" > + "\n" > + "\n" > + "\n")); > +} > + > +static void > +test_abc () > +{ > + style_manager sm; > + canvas c (canvas::size_t (3, 3), sm); > + c.paint (canvas::coord_t (0, 0), styled_unichar ('A')); > + c.paint (canvas::coord_t (1, 1), styled_unichar ('B')); > + c.paint (canvas::coord_t (2, 2), styled_unichar ('C')); > + > + ASSERT_CANVAS_STREQ (c, false, > + "A\n B\n C\n"); > +} > + > +static void > +test_debug_fill () > +{ > + style_manager sm; > + canvas c (canvas::size_t (5, 3), sm); > + c.debug_fill(); > + ASSERT_CANVAS_STREQ (c, false, > + ("*****\n" > + "*****\n" > + "*****\n")); > +} > + > +static void > +test_text () > +{ > + style_manager sm; > + canvas c (canvas::size_t (6, 1), sm); > + c.paint_text (canvas::coord_t (0, 0), styled_string (sm, "012345")); > + ASSERT_CANVAS_STREQ (c, false, > + ("012345\n")); > + > + /* Paint an emoji character that should occupy two canvas columns when > + printed. */ > + c.paint_text (canvas::coord_t (2, 0), styled_string ((cppchar_t)0x1f64= 2)); > + ASSERT_CANVAS_STREQ (c, false, > + ("01=F0=9F=99=8245\n")); > +} > + > +static void > +test_circle () > +{ > + canvas::size_t sz (30, 30); > + style_manager sm; > + canvas canvas (sz, sm); > + canvas::coord_t center (sz.w / 2, sz.h / 2); > + const int radius =3D 12; > + const int radius_squared =3D radius * radius; > + for (int x =3D 0; x < sz.w; x++) > + for (int y =3D 0; y < sz.h; y++) > + { > + int dx =3D x - center.x; > + int dy =3D y - center.y; > + char ch =3D "AB"[(x + y) % 2]; > + if (dx * dx + dy * dy < radius_squared) > + canvas.paint (canvas::coord_t (x, y), styled_unichar (ch)); > + } > + ASSERT_CANVAS_STREQ > + (canvas, false, > + ("\n" > + "\n" > + "\n" > + "\n" > + " BABABABAB\n" > + " ABABABABABABA\n" > + " ABABABABABABABA\n" > + " ABABABABABABABABA\n" > + " ABABABABABABABABABA\n" > + " ABABABABABABABABABABA\n" > + " BABABABABABABABABABAB\n" > + " BABABABABABABABABABABAB\n" > + " ABABABABABABABABABABABA\n" > + " BABABABABABABABABABABAB\n" > + " ABABABABABABABABABABABA\n" > + " BABABABABABABABABABABAB\n" > + " ABABABABABABABABABABABA\n" > + " BABABABABABABABABABABAB\n" > + " ABABABABABABABABABABABA\n" > + " BABABABABABABABABABABAB\n" > + " BABABABABABABABABABAB\n" > + " ABABABABABABABABABABA\n" > + " ABABABABABABABABABA\n" > + " ABABABABABABABABA\n" > + " ABABABABABABABA\n" > + " ABABABABABABA\n" > + " BABABABAB\n" > + "\n" > + "\n" > + "\n")); > +} > + > +static void > +test_color_circle () > +{ > + const canvas::size_t sz (10, 10); > + const canvas::coord_t center (sz.w / 2, sz.h / 2); > + const int outer_r2 =3D 25; > + const int inner_r2 =3D 10; > + style_manager sm; > + canvas c (sz, sm); > + for (int x =3D 0; x < sz.w; x++) > + for (int y =3D 0; y < sz.h; y++) > + { > + const int dist_from_center_squared > + =3D ((x - center.x) * (x - center.x) + (y - center.y) * (y - center.y= )); > + if (dist_from_center_squared < outer_r2) > + { > + style s; > + if (dist_from_center_squared < inner_r2) > + s.m_fg_color =3D style::named_color::RED; > + else > + s.m_fg_color =3D style::named_color::GREEN; > + c.paint (canvas::coord_t (x, y), > + styled_unichar ('*', false, sm.get_or_create_id (s))); > + } > + } > + ASSERT_EQ (sm.get_num_styles (), 3); > + ASSERT_CANVAS_STREQ > + (c, false, > + ("\n" > + " *****\n" > + " *******\n" > + " *********\n" > + " *********\n" > + " *********\n" > + " *********\n" > + " *********\n" > + " *******\n" > + " *****\n")); > + ASSERT_CANVAS_STREQ > + (c, true, > + ("\n" > + " =1B[32m=1B[K*****=1B[m=1B[K\n" > + " =1B[32m=1B[K***=1B[31m=1B[K*=1B[32m=1B[K***=1B[m=1B[K\n" > + " =1B[32m=1B[K**=1B[31m=1B[K*****=1B[32m=1B[K**=1B[m=1B[K\n" > + " =1B[32m=1B[K**=1B[31m=1B[K*****=1B[32m=1B[K**=1B[m=1B[K\n" > + " =1B[32m=1B[K*=1B[31m=1B[K*******=1B[32m=1B[K*=1B[m=1B[K\n" > + " =1B[32m=1B[K**=1B[31m=1B[K*****=1B[32m=1B[K**=1B[m=1B[K\n" > + " =1B[32m=1B[K**=1B[31m=1B[K*****=1B[32m=1B[K**=1B[m=1B[K\n" > + " =1B[32m=1B[K***=1B[31m=1B[K*=1B[32m=1B[K***=1B[m=1B[K\n" > + " =1B[32m=1B[K*****=1B[m=1B[K\n")); > +} > + > +static void > +test_bold () > +{ > + auto_fix_quotes fix_quotes; > + style_manager sm; > + styled_string s (styled_string::from_fmt (sm, nullptr, > + "before %qs after", "foo")); > + canvas c (canvas::size_t (s.calc_canvas_width (), 1), sm); > + c.paint_text (canvas::coord_t (0, 0), s); > + ASSERT_CANVAS_STREQ (c, false, > + "before `foo' after\n"); > + ASSERT_CANVAS_STREQ (c, true, > + "before `=1B[00;01m=1B[Kfoo=1B[00m=1B[K' after\n"); > +} > + > +static void > +test_emoji () > +{ > + style_manager sm; > + styled_string s (0x26A0, /* U+26A0 WARNING SIGN. */ > + true); > + canvas c (canvas::size_t (s.calc_canvas_width (), 1), sm); > + c.paint_text (canvas::coord_t (0, 0), s); > + ASSERT_CANVAS_STREQ (c, false, "=E2=9A=A0=EF=B8=8F\n"); > + ASSERT_CANVAS_STREQ (c, true, "=E2=9A=A0=EF=B8=8F\n"); > +} > + > +static void > +test_emoji_2 () > +{ > + style_manager sm; > + styled_string s; > + s.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN. */ > + true)); > + s.append (styled_string (sm, "test")); > + ASSERT_EQ (s.size (), 5); > + ASSERT_EQ (s.calc_canvas_width (), 5); > + canvas c (canvas::size_t (s.calc_canvas_width (), 1), sm); > + c.paint_text (canvas::coord_t (0, 0), s); > + ASSERT_CANVAS_STREQ (c, false, > + /* U+26A0 WARNING SIGN, as UTF-8: 0xE2 0x9A 0xA0. */ > + "\xE2\x9A\xA0" > + /* U+FE0F VARIATION SELECTOR-16, as UTF-8: 0xEF 0xB8 0x8F. */ > + "\xEF\xB8\x8F" > + "test\n"); > +} > + > +static void > +test_canvas_urls () > +{ > + style_manager sm; > + canvas canvas (canvas::size_t (9, 3), sm); > + styled_string foo_ss (sm, "foo"); > + foo_ss.set_url (sm, "https://www.example.com/foo"); > + styled_string bar_ss (sm, "bar"); > + bar_ss.set_url (sm, "https://www.example.com/bar"); > + canvas.paint_text(canvas::coord_t (1, 1), foo_ss); > + canvas.paint_text(canvas::coord_t (5, 1), bar_ss); > + > + ASSERT_CANVAS_STREQ (canvas, false, > + ("\n" > + " foo bar\n" > + "\n")); > + { > + pretty_printer pp; > + pp_show_color (&pp) =3D true; > + pp.url_format =3D URL_FORMAT_ST; > + assert_canvas_streq (SELFTEST_LOCATION, canvas, &pp, > + (/* Line 1. */ > + "\n" > + /* Line 2. */ > + " " > + "\33]8;;https://www.example.com/foo\33\\foo\33]8;;\33\\" > + " " > + "\33]8;;https://www.example.com/bar\33\\bar\33]8;;\33\\" > + "\n" > + /* Line 3. */ > + "\n")); > + } > + > + { > + pretty_printer pp; > + pp_show_color (&pp) =3D true; > + pp.url_format =3D URL_FORMAT_BEL; > + assert_canvas_streq (SELFTEST_LOCATION, canvas, &pp, > + (/* Line 1. */ > + "\n" > + /* Line 2. */ > + " " > + "\33]8;;https://www.example.com/foo\afoo\33]8;;\a" > + " " > + "\33]8;;https://www.example.com/bar\abar\33]8;;\a" > + "\n" > + /* Line 3. */ > + "\n")); > + } > +} > + > +/* Run all selftests in this file. */ > + > +void > +text_art_canvas_cc_tests () > +{ > + test_blank (); > + test_abc (); > + test_debug_fill (); > + test_text (); > + test_circle (); > + test_color_circle (); > + test_bold (); > + test_emoji (); > + test_emoji_2 (); > + test_canvas_urls (); > +} > + > +} // namespace selftest > + > + > +#endif /* #if CHECKING_P */ > diff --git a/gcc/text-art/canvas.h b/gcc/text-art/canvas.h > new file mode 100644 > index 00000000000..495497754f5 > --- /dev/null > +++ b/gcc/text-art/canvas.h > @@ -0,0 +1,74 @@ > +/* Canvas for random-access procedural text art. > + Copyright (C) 2023 Free Software Foundation, Inc. > + Contributed by David Malcolm . > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it > +under the terms of the GNU General Public License as published by > +the Free Software Foundation; either version 3, or (at your option) > +any later version. > + > +GCC is distributed in the hope that it will be useful, but > +WITHOUT ANY WARRANTY; without even the implied warranty of > +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > +General Public License for more details. > + > +You should have received a copy of the GNU General Public License > +along with GCC; see the file COPYING3. If not see > +. */ > + > +#ifndef GCC_TEXT_ART_CANVAS_H > +#define GCC_TEXT_ART_CANVAS_H > + > +#include "text-art/types.h" > + > +namespace text_art { > + > +class canvas; > + > +/* A 2 dimensional grid of text cells (a "canvas"), which > + can be written to ("painted") via random access, and then > + written out to a pretty_printer once the picture is complete. > + > + Each text cell can be styled independently (colorization, > + URLs, etc). */ > + > +class canvas > +{ > + public: > + typedef styled_unichar cell_t; > + typedef size size_t; > + typedef coord coord_t; > + typedef range range_t; > + typedef rect rect_t; > + > + canvas (size_t size, const style_manager &style_mgr); > + > + size_t get_size () const { return m_cells.get_size (); } > + > + void paint (coord_t coord, cell_t c); > + void paint_text (coord_t coord, const styled_string &text); > + > + void fill (rect_t rect, cell_t c); > + void debug_fill (); > + > + void print_to_pp (pretty_printer *pp, > + const char *per_line_prefix =3D NULL) const; > + void debug (bool styled) const; > + > + const cell_t &get (coord_t coord) const > + { > + return m_cells.get (coord); > + } > + > + private: > + int get_final_x_in_row (int y) const; > + > + array2 m_cells; > + const style_manager &m_style_mgr; > +}; > + > +} // namespace text_art > + > +#endif /* GCC_TEXT_ART_CANVAS_H */ > diff --git a/gcc/text-art/ruler.cc b/gcc/text-art/ruler.cc > new file mode 100644 > index 00000000000..80c623f77ba > --- /dev/null > +++ b/gcc/text-art/ruler.cc > @@ -0,0 +1,723 @@ > +/* Classes for printing labelled rulers. > + Copyright (C) 2023 Free Software Foundation, Inc. > + Contributed by David Malcolm . > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it under > +the terms of the GNU General Public License as published by the Free > +Software Foundation; either version 3, or (at your option) any later > +version. > + > +GCC is distributed in the hope that it will be useful, but WITHOUT ANY > +WARRANTY; without even the implied warranty of MERCHANTABILITY or > +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License > +for more details. > + > +You should have received a copy of the GNU General Public License > +along with GCC; see the file COPYING3. If not see > +. */ > + > +#include "config.h" > +#define INCLUDE_ALGORITHM > +#include "system.h" > +#include "coretypes.h" > +#include "pretty-print.h" > +#include "selftest.h" > +#include "text-art/selftests.h" > +#include "text-art/ruler.h" > +#include "text-art/theme.h" > + > +using namespace text_art; > + > +void > +x_ruler::add_label (const canvas::range_t &r, > + styled_string text, > + style::id_t style_id, > + label_kind kind) > +{ > + m_labels.push_back (label (r, std::move (text), style_id, kind)); > + m_has_layout =3D false; > +} > + > +int > +x_ruler::get_canvas_y (int rel_y) const > +{ > + gcc_assert (rel_y >=3D 0); > + gcc_assert (rel_y < m_size.h); > + switch (m_label_dir) > + { > + default: > + gcc_unreachable (); > + case label_dir::ABOVE: > + return m_size.h - (rel_y + 1); > + case label_dir::BELOW: > + return rel_y; > + } > +} > + > +void > +x_ruler::paint_to_canvas (canvas &canvas, > + canvas::coord_t offset, > + const theme &theme) > +{ > + ensure_layout (); > + > + if (0) > + canvas.fill (canvas::rect_t (offset, m_size), > + canvas::cell_t ('*')); > + > + for (size_t idx =3D 0; idx < m_labels.size (); idx++) > + { > + const label &iter_label =3D m_labels[idx]; > + > + /* Paint the ruler itself. */ > + const int ruler_rel_y =3D get_canvas_y (0); > + for (int rel_x =3D iter_label.m_range.start; > + rel_x < iter_label.m_range.next; > + rel_x++) > + { > + enum theme::cell_kind kind =3D theme::cell_kind::X_RULER_MIDDLE; > + > + if (rel_x =3D=3D iter_label.m_range.start) > + { > + kind =3D theme::cell_kind::X_RULER_LEFT_EDGE; > + if (idx > 0) > + { > + const label &prev_label =3D m_labels[idx - 1]; > + if (prev_label.m_range.get_max () =3D=3D iter_label.m_range.start) > + kind =3D theme::cell_kind::X_RULER_INTERNAL_EDGE; > + } > + } > + else if (rel_x =3D=3D iter_label.m_range.get_max ()) > + kind =3D theme::cell_kind::X_RULER_RIGHT_EDGE; > + else if (rel_x =3D=3D iter_label.m_connector_x) > + { > + switch (m_label_dir) > + { > + default: > + gcc_unreachable (); > + case label_dir::ABOVE: > + kind =3D theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_ABOVE; > + break; > + case label_dir::BELOW: > + kind =3D theme::cell_kind::X_RULER_CONNECTOR_TO_LABEL_BELOW; > + break; > + } > + } > + canvas.paint (canvas::coord_t (rel_x, ruler_rel_y) + offset, > + theme.get_cell (kind, iter_label.m_style_id)); > + } > + > + /* Paint the connector to the text. */ > + for (int connector_rel_y =3D 1; > + connector_rel_y < iter_label.m_text_rect.get_min_y (); > + connector_rel_y++) > + { > + canvas.paint > + ((canvas::coord_t (iter_label.m_connector_x, > + get_canvas_y (connector_rel_y)) > + + offset), > + theme.get_cell (theme::cell_kind::X_RULER_VERTICAL_CONNECTOR, > + iter_label.m_style_id)); > + } > + > + /* Paint the text. */ > + switch (iter_label.m_kind) > + { > + default: > + gcc_unreachable (); > + case x_ruler::label_kind::TEXT: > + canvas.paint_text > + ((canvas::coord_t (iter_label.m_text_rect.get_min_x (), > + get_canvas_y (iter_label.m_text_rect.get_min_y ())) > + + offset), > + iter_label.m_text); > + break; > + > + case x_ruler::label_kind::TEXT_WITH_BORDER: > + { > + const canvas::range_t rel_x_range > + (iter_label.m_text_rect.get_x_range ()); > + > + enum theme::cell_kind inner_left_kind; > + enum theme::cell_kind inner_connector_kind; > + enum theme::cell_kind inner_right_kind; > + enum theme::cell_kind outer_left_kind; > + enum theme::cell_kind outer_right_kind; > + > + switch (m_label_dir) > + { > + default: > + gcc_unreachable (); > + case label_dir::ABOVE: > + outer_left_kind =3D theme::cell_kind::TEXT_BORDER_TOP_LEFT; > + outer_right_kind =3D theme::cell_kind::TEXT_BORDER_TOP_RIGHT; > + inner_left_kind =3D theme::cell_kind::TEXT_BORDER_BOTTOM_LEFT; > + inner_connector_kind =3D theme::cell_kind::X_RULER_CONNECTOR_TO_LABE= L_BELOW; > + inner_right_kind =3D theme::cell_kind::TEXT_BORDER_BOTTOM_RIGHT; > + break; > + case label_dir::BELOW: > + inner_left_kind =3D theme::cell_kind::TEXT_BORDER_TOP_LEFT; > + inner_connector_kind =3D theme::cell_kind::X_RULER_CONNECTOR_TO_LABE= L_ABOVE; > + inner_right_kind =3D theme::cell_kind::TEXT_BORDER_TOP_RIGHT; > + outer_left_kind =3D theme::cell_kind::TEXT_BORDER_BOTTOM_LEFT; > + outer_right_kind =3D theme::cell_kind::TEXT_BORDER_BOTTOM_RIGHT; > + break; > + } > + /* Inner border. */ > + { > + const int rel_canvas_y > + =3D get_canvas_y (iter_label.m_text_rect.get_min_y ()); > + /* Left corner. */ > + canvas.paint ((canvas::coord_t (rel_x_range.get_min (), > + rel_canvas_y) > + + offset), > + theme.get_cell (inner_left_kind, > + iter_label.m_style_id)); > + /* Edge. */ > + const canvas::cell_t edge_border_cell > + =3D theme.get_cell (theme::cell_kind::TEXT_BORDER_HORIZONTAL, > + iter_label.m_style_id); > + const canvas::cell_t connector_border_cell > + =3D theme.get_cell (inner_connector_kind, > + iter_label.m_style_id); > + for (int rel_x =3D rel_x_range.get_min () + 1; > + rel_x < rel_x_range.get_max (); > + rel_x++) > + if (rel_x =3D=3D iter_label.m_connector_x) > + canvas.paint ((canvas::coord_t (rel_x, rel_canvas_y) > + + offset), > + connector_border_cell); > + else > + canvas.paint ((canvas::coord_t (rel_x, rel_canvas_y) > + + offset), > + edge_border_cell); > + > + /* Right corner. */ > + canvas.paint ((canvas::coord_t (rel_x_range.get_max (), > + rel_canvas_y) > + + offset), > + theme.get_cell (inner_right_kind, > + iter_label.m_style_id)); > + } > + > + { > + const int rel_canvas_y > + =3D get_canvas_y (iter_label.m_text_rect.get_min_y () + 1); > + const canvas::cell_t border_cell > + =3D theme.get_cell (theme::cell_kind::TEXT_BORDER_VERTICAL, > + iter_label.m_style_id); > + > + /* Left border. */ > + canvas.paint ((canvas::coord_t (rel_x_range.get_min (), > + rel_canvas_y) > + + offset), > + border_cell); > + /* Text. */ > + canvas.paint_text ((canvas::coord_t (rel_x_range.get_min () + 1, > + rel_canvas_y) > + + offset), > + iter_label.m_text); > + /* Right border. */ > + canvas.paint ((canvas::coord_t (rel_x_range.get_max (), > + rel_canvas_y) > + + offset), > + border_cell); > + } > + > + /* Outer border. */ > + { > + const int rel_canvas_y > + =3D get_canvas_y (iter_label.m_text_rect.get_max_y ()); > + /* Left corner. */ > + canvas.paint ((canvas::coord_t (rel_x_range.get_min (), > + rel_canvas_y) > + + offset), > + theme.get_cell (outer_left_kind, > + iter_label.m_style_id)); > + /* Edge. */ > + const canvas::cell_t border_cell > + =3D theme.get_cell (theme::cell_kind::TEXT_BORDER_HORIZONTAL, > + iter_label.m_style_id); > + for (int rel_x =3D rel_x_range.get_min () + 1; > + rel_x < rel_x_range.get_max (); > + rel_x++) > + canvas.paint ((canvas::coord_t (rel_x, rel_canvas_y) > + + offset), > + border_cell); > + > + /* Right corner. */ > + canvas.paint ((canvas::coord_t (rel_x_range.get_max (), > + rel_canvas_y) > + + offset), > + theme.get_cell (outer_right_kind, > + iter_label.m_style_id)); > + } > + } > + break; > + } > + } > +} > + > +DEBUG_FUNCTION void > +x_ruler::debug (const style_manager &sm) > +{ > + canvas c (get_size (), sm); > + paint_to_canvas (c, canvas::coord_t (0, 0), unicode_theme ()); > + c.debug (true); > +} > + > +x_ruler::label::label (const canvas::range_t &range, > + styled_string text, > + style::id_t style_id, > + label_kind kind) > +: m_range (range), > + m_text (std::move (text)), > + m_style_id (style_id), > + m_kind (kind), > + m_text_rect (canvas::coord_t (0, 0), > + canvas::size_t (m_text.calc_canvas_width (), 1)), > + m_connector_x ((m_range.get_min () + m_range.get_max ()) / 2) > +{ > + if (kind =3D=3D label_kind::TEXT_WITH_BORDER) > + { > + m_text_rect.m_size.w +=3D 2; > + m_text_rect.m_size.h +=3D 2; > + } > +} > + > +bool > +x_ruler::label::operator< (const label &other) const > +{ > + int cmp =3D m_range.start - other.m_range.start; > + if (cmp) > + return cmp < 0; > + return m_range.next < other.m_range.next; > +} > + > +void > +x_ruler::ensure_layout () > +{ > + if (m_has_layout) > + return; > + update_layout (); > + m_has_layout =3D true; > +} > + > +void > +x_ruler::update_layout () > +{ > + if (m_labels.empty ()) > + return; > + > + std::sort (m_labels.begin (), m_labels.end ()); > + > + /* Place labels. */ > + int ruler_width =3D m_labels.back ().m_range.get_next (); > + int width_with_labels =3D ruler_width; > + > + /* Get x coordinates of text parts of each label > + (m_text_rect.m_top_left.x for each label). */ > + for (size_t idx =3D 0; idx < m_labels.size (); idx++) > + { > + label &iter_label =3D m_labels[idx]; > + /* Attempt to center the text label. */ > + int min_x; > + if (idx > 0) > + { > + /* ...but don't overlap with the connector to the left. */ > + int left_neighbor_connector_x =3D m_labels[idx - 1].m_connector_x; > + min_x =3D left_neighbor_connector_x + 1; > + } > + else > + { > + /* ...or go beyond the leftmost column. */ > + min_x =3D 0; > + } > + int connector_x =3D iter_label.m_connector_x; > + int centered_x > + =3D connector_x - ((int)iter_label.m_text_rect.get_width () / 2); > + int text_x =3D std::max (min_x, centered_x); > + iter_label.m_text_rect.m_top_left.x =3D text_x; > + } > + > + /* Now walk backwards trying to place them vertically, > + setting m_text_rect.m_top_left.y for each label, > + consolidating the rows where possible. > + The y cooordinates are stored with respect to label_dir::BELOW. */ > + int label_y =3D 2; > + for (int idx =3D m_labels.size () - 1; idx >=3D 0; idx--) > + { > + label &iter_label =3D m_labels[idx]; > + /* Does it fit on the same row as the text label to the right? */ > + size_t text_len =3D iter_label.m_text_rect.get_width (); > + /* Get the x-coord of immediately beyond iter_label's text. */ > + int next_x =3D iter_label.m_text_rect.get_min_x () + text_len; > + if (idx < (int)m_labels.size () - 1) > + { > + if (next_x >=3D m_labels[idx + 1].m_text_rect.get_min_x ()) > + { > + /* If not, start a new row. */ > + label_y +=3D m_labels[idx + 1].m_text_rect.get_height (); > + } > + } > + iter_label.m_text_rect.m_top_left.y =3D label_y; > + width_with_labels =3D std::max (width_with_labels, next_x); > + } > + > + m_size =3D canvas::size_t (width_with_labels, > + label_y + m_labels[0].m_text_rect.get_height ()); > +} > + > +#if CHECKING_P > + > +namespace selftest { > + > +static void > +assert_x_ruler_streq (const location &loc, > + x_ruler &ruler, > + const theme &theme, > + const style_manager &sm, > + bool styled, > + const char *expected_str) > +{ > + canvas c (ruler.get_size (), sm); > + ruler.paint_to_canvas (c, canvas::coord_t (0, 0), theme); > + if (0) > + c.debug (styled); > + assert_canvas_streq (loc, c, styled, expected_str); > +} > + > +#define ASSERT_X_RULER_STREQ(RULER, THEME, SM, STYLED, EXPECTED_STR) \ > + SELFTEST_BEGIN_STMT \ > + assert_x_ruler_streq ((SELFTEST_LOCATION), \ > + (RULER), \ > + (THEME), \ > + (SM), \ > + (STYLED), \ > + (EXPECTED_STR)); \ > + SELFTEST_END_STMT > + > +static void > +test_single () > +{ > + style_manager sm; > + x_ruler r (x_ruler::label_dir::BELOW); > + r.add_label (canvas::range_t (0, 11), styled_string (sm, "foo"), > + style::id_plain, x_ruler::label_kind::TEXT); > + ASSERT_X_RULER_STREQ > + (r, ascii_theme (), sm, true, > + ("|~~~~+~~~~|\n" > + " |\n" > + " foo\n")); > + ASSERT_X_RULER_STREQ > + (r, unicode_theme (), sm, true, > + ("=E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=A4\n" > + " =E2=94=82\n" > + " foo\n")); > +} > + > +static void > +test_single_above () > +{ > + style_manager sm; > + x_ruler r (x_ruler::label_dir::ABOVE); > + r.add_label (canvas::range_t (0, 11), styled_string (sm, "hello world"= ), > + style::id_plain); > + ASSERT_X_RULER_STREQ > + (r, ascii_theme (), sm, true, > + ("hello world\n" > + " |\n" > + "|~~~~+~~~~|\n")); > + ASSERT_X_RULER_STREQ > + (r, unicode_theme (), sm, true, > + ("hello world\n" > + " =E2=94=82\n" > + "=E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=A4\n")); > +} > + > +static void > +test_multiple_contiguous () > +{ > + style_manager sm; > + x_ruler r (x_ruler::label_dir::BELOW); > + r.add_label (canvas::range_t (0, 11), styled_string (sm, "foo"), > + style::id_plain); > + r.add_label (canvas::range_t (10, 16), styled_string (sm, "bar"), > + style::id_plain); > + ASSERT_X_RULER_STREQ > + (r, ascii_theme (), sm, true, > + ("|~~~~+~~~~|~+~~|\n" > + " | |\n" > + " foo bar\n")); > + ASSERT_X_RULER_STREQ > + (r, unicode_theme (), sm, true, > + ("=E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2= =94=A4\n" > + " =E2=94=82 =E2=94=82\n" > + " foo bar\n")); > +} > + > +static void > +test_multiple_contiguous_above () > +{ > + style_manager sm; > + x_ruler r (x_ruler::label_dir::ABOVE); > + r.add_label (canvas::range_t (0, 11), styled_string (sm, "foo"), > + style::id_plain); > + r.add_label (canvas::range_t (10, 16), styled_string (sm, "bar"), > + style::id_plain); > + ASSERT_X_RULER_STREQ > + (r, ascii_theme (), sm, true, > + (" foo bar\n" > + " | |\n" > + "|~~~~+~~~~|~+~~|\n")); > + ASSERT_X_RULER_STREQ > + (r, unicode_theme (), sm, true, > + (" foo bar\n" > + " =E2=94=82 =E2=94=82\n" > + "=E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=B4=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2= =94=A4\n")); > +} > + > +static void > +test_multiple_contiguous_abutting_labels () > +{ > + style_manager sm; > + x_ruler r (x_ruler::label_dir::BELOW); > + r.add_label (canvas::range_t (0, 11), styled_string (sm, "12345678"), > + style::id_plain); > + r.add_label (canvas::range_t (10, 16), styled_string (sm, "1234678"), > + style::id_plain); > + ASSERT_X_RULER_STREQ > + (r, unicode_theme (), sm, true, > + ("=E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2= =94=A4\n" > + " =E2=94=82 =E2=94=82\n" > + " =E2=94=82 1234678\n" > + " 12345678\n")); > +} > + > +static void > +test_multiple_contiguous_overlapping_labels () > +{ > + style_manager sm; > + x_ruler r (x_ruler::label_dir::BELOW); > + r.add_label (canvas::range_t (0, 11), styled_string (sm, "123456789"), > + style::id_plain); > + r.add_label (canvas::range_t (10, 16), styled_string (sm, "12346789"), > + style::id_plain); > + ASSERT_X_RULER_STREQ > + (r, unicode_theme (), sm, true, > + ("=E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2= =94=A4\n" > + " =E2=94=82 =E2=94=82\n" > + " =E2=94=82 12346789\n" > + " 123456789\n")); > +} > +static void > +test_abutting_left_border () > +{ > + style_manager sm; > + x_ruler r (x_ruler::label_dir::BELOW); > + r.add_label (canvas::range_t (0, 6), > + styled_string (sm, "this is a long label"), > + style::id_plain); > + ASSERT_X_RULER_STREQ > + (r, unicode_theme (), sm, true, > + ("=E2=94=9C=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=A4\n" > + " =E2=94=82\n" > + "this is a long label\n")); > +} > + > +static void > +test_too_long_to_consolidate_vertically () > +{ > + style_manager sm; > + x_ruler r (x_ruler::label_dir::BELOW); > + r.add_label (canvas::range_t (0, 11), > + styled_string (sm, "long string A"), > + style::id_plain); > + r.add_label (canvas::range_t (10, 16), > + styled_string (sm, "long string B"), > + style::id_plain); > + ASSERT_X_RULER_STREQ > + (r, unicode_theme (), sm, true, > + ("=E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2= =94=A4\n" > + " =E2=94=82 =E2=94=82\n" > + " =E2=94=82long string B\n" > + "long string A\n")); > +} > + > +static void > +test_abutting_neighbor () > +{ > + style_manager sm; > + x_ruler r (x_ruler::label_dir::BELOW); > + r.add_label (canvas::range_t (0, 11), > + styled_string (sm, "very long string A"), > + style::id_plain); > + r.add_label (canvas::range_t (10, 16), > + styled_string (sm, "very long string B"), > + style::id_plain); > + ASSERT_X_RULER_STREQ > + (r, unicode_theme (), sm, true, > + ("=E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=AC=E2=94=80= =E2=94=80=E2=94=80=E2=94=80=E2=94=BC=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2= =94=A4\n" > + " =E2=94=82 =E2=94=82\n" > + " =E2=94=82very long string B\n" > + "very long string A\n")); > +} > + > +static void > +test_gaps () > +{ > + style_manager sm; > + x_ruler r (x_ruler::label_dir::BELOW); > + r.add_label (canvas::range_t (0, 5), > + styled_string (sm, "foo"), > + style::id_plain); > + r.add_label (canvas::range_t (10, 15), > + styled_string (sm, "bar"), > + style::id_plain); > + ASSERT_X_RULER_STREQ > + (r, ascii_theme (), sm, true, > + ("|~+~| |~+~|\n" > + " | |\n" > + " foo bar\n")); > +} > + > +static void > +test_styled () > +{ > + style_manager sm; > + style s1, s2; > + s1.m_bold =3D true; > + s1.m_fg_color =3D style::named_color::YELLOW; > + s2.m_bold =3D true; > + s2.m_fg_color =3D style::named_color::BLUE; > + style::id_t sid1 =3D sm.get_or_create_id (s1); > + style::id_t sid2 =3D sm.get_or_create_id (s2); > + > + x_ruler r (x_ruler::label_dir::BELOW); > + r.add_label (canvas::range_t (0, 5), styled_string (sm, "foo"), sid1); > + r.add_label (canvas::range_t (10, 15), styled_string (sm, "bar"), sid2= ); > + ASSERT_X_RULER_STREQ > + (r, ascii_theme (), sm, true, > + ("=1B[00;01;33m=1B[K|~+~|=1B[00m=1B[K =1B[00;01;34m=1B[K|~+~|= =1B[00m=1B[K\n" > + " =1B[00;01;33m=1B[K|=1B[00m=1B[K =1B[00;01;34m=1B[K|=1B[= 00m=1B[K\n" > + " foo bar\n")); > +} > + > +static void > +test_borders () > +{ > + style_manager sm; > + { > + x_ruler r (x_ruler::label_dir::BELOW); > + r.add_label (canvas::range_t (0, 5), > + styled_string (sm, "label 1"), > + style::id_plain, > + x_ruler::label_kind::TEXT_WITH_BORDER); > + r.add_label (canvas::range_t (10, 15), > + styled_string (sm, "label 2"), > + style::id_plain); > + r.add_label (canvas::range_t (20, 25), > + styled_string (sm, "label 3"), > + style::id_plain, > + x_ruler::label_kind::TEXT_WITH_BORDER); > + ASSERT_X_RULER_STREQ > + (r, ascii_theme (), sm, true, > + "|~+~| |~+~| |~+~|\n" > + " | | |\n" > + " | label 2 +---+---+\n" > + "+-+-----+ |label 3|\n" > + "|label 1| +-------+\n" > + "+-------+\n"); > + ASSERT_X_RULER_STREQ > + (r, unicode_theme (), sm, true, > + "=E2=94=9C=E2=94=80=E2=94=AC=E2=94=80=E2=94=A4 =E2=94=9C=E2= =94=80=E2=94=AC=E2=94=80=E2=94=A4 =E2=94=9C=E2=94=80=E2=94=AC=E2=94=80= =E2=94=A4\n" > + " =E2=94=82 =E2=94=82 =E2=94=82\n" > + " =E2=94=82 label 2 =E2=95=AD=E2=94=80=E2=94=80=E2=94=80= =E2=94=B4=E2=94=80=E2=94=80=E2=94=80=E2=95=AE\n" > + "=E2=95=AD=E2=94=80=E2=94=B4=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=95=AE =E2=94=82label 3=E2=94=82\n" > + "=E2=94=82label 1=E2=94=82 =E2=95=B0=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=95=AF\n" > + "=E2=95=B0=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=95=AF\n"); > + } > + { > + x_ruler r (x_ruler::label_dir::ABOVE); > + r.add_label (canvas::range_t (0, 5), > + styled_string (sm, "label 1"), > + style::id_plain, > + x_ruler::label_kind::TEXT_WITH_BORDER); > + r.add_label (canvas::range_t (10, 15), > + styled_string (sm, "label 2"), > + style::id_plain); > + r.add_label (canvas::range_t (20, 25), > + styled_string (sm, "label 3"), > + style::id_plain, > + x_ruler::label_kind::TEXT_WITH_BORDER); > + ASSERT_X_RULER_STREQ > + (r, ascii_theme (), sm, true, > + "+-------+\n" > + "|label 1| +-------+\n" > + "+-+-----+ |label 3|\n" > + " | label 2 +---+---+\n" > + " | | |\n" > + "|~+~| |~+~| |~+~|\n"); > + ASSERT_X_RULER_STREQ > + (r, unicode_theme (), sm, true, > + "=E2=95=AD=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=95=AE\n" > + "=E2=94=82label 1=E2=94=82 =E2=95=AD=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=95=AE\n" > + "=E2=95=B0=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=95=AF =E2=94=82label 3=E2=94=82\n" > + " =E2=94=82 label 2 =E2=95=B0=E2=94=80=E2=94=80=E2=94=80= =E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=95=AF\n" > + " =E2=94=82 =E2=94=82 =E2=94=82\n" > + "=E2=94=9C=E2=94=80=E2=94=B4=E2=94=80=E2=94=A4 =E2=94=9C=E2= =94=80=E2=94=B4=E2=94=80=E2=94=A4 =E2=94=9C=E2=94=80=E2=94=B4=E2=94=80= =E2=94=A4\n"); > + } > +} > + > +static void > +test_emoji () > +{ > + style_manager sm; > + > + styled_string s; > + s.append (styled_string (0x26A0, /* U+26A0 WARNING SIGN. */ > + true)); > + s.append (styled_string (sm, " ")); > + s.append (styled_string (sm, "this is a warning")); > + > + x_ruler r (x_ruler::label_dir::BELOW); > + r.add_label (canvas::range_t (0, 5), > + std::move (s), > + style::id_plain, > + x_ruler::label_kind::TEXT_WITH_BORDER); > + > + ASSERT_X_RULER_STREQ > + (r, ascii_theme (), sm, true, > + "|~+~|\n" > + " |\n" > + "+-+------------------+\n" > + "|=E2=9A=A0=EF=B8=8F this is a warning|\n" > + "+--------------------+\n"); > +} > + > +/* Run all selftests in this file. */ > + > +void > +text_art_ruler_cc_tests () > +{ > + test_single (); > + test_single_above (); > + test_multiple_contiguous (); > + test_multiple_contiguous_above (); > + test_multiple_contiguous_abutting_labels (); > + test_multiple_contiguous_overlapping_labels (); > + test_abutting_left_border (); > + test_too_long_to_consolidate_vertically (); > + test_abutting_neighbor (); > + test_gaps (); > + test_styled (); > + test_borders (); > + test_emoji (); > +} > + > +} // namespace selftest > + > + > +#endif /* #if CHECKING_P */ > diff --git a/gcc/text-art/ruler.h b/gcc/text-art/ruler.h > new file mode 100644 > index 00000000000..31f53549836 > --- /dev/null > +++ b/gcc/text-art/ruler.h > @@ -0,0 +1,125 @@ > +/* Classes for printing labelled rulers. > + Copyright (C) 2023 Free Software Foundation, Inc. > + Contributed by David Malcolm . > + > +This file is part of GCC. > + > +GCC is free software; you can redistribute it and/or modify it > +under the terms of the GNU General Public License as published by > +the Free Software Foundation; either version 3, or (at your option) > +any later version. > + > +GCC is distributed in the hope that it will be useful, but > +WITHOUT ANY WARRANTY; without even the implied warranty of > +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > +General Public License for more details. > + > +You should have received a copy of the GNU General Public License > +along with GCC; see the file COPYING3. If not see > +. */ > + > +#ifndef GCC_TEXT_ART_RULER_H > +#define GCC_TEXT_ART_RULER_H > + > +#include "text-art/canvas.h" > + > +namespace text_art { > + > +/* A way to annotate a series of ranges of canvas coordinates > + with text labels either above or, in this example, below: > + =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=A4 > + =E2=94=82 =E2=94=82 =E2=94=82 > + label A label B label C > + with logic to ensure that the text labels don't overlap > + when printed. */ > + > +class x_ruler > +{ > + public: > + enum class label_dir { ABOVE, BELOW }; > + enum class label_kind > + { > + TEXT, > + TEXT_WITH_BORDER > + }; > + > + x_ruler (label_dir dir) > + : m_label_dir (dir), > + m_size (canvas::size_t (0, 0)), > + m_has_layout (false) > + {} > + > + void add_label (const canvas::range_t &r, > + styled_string text, > + style::id_t style_id, > + label_kind kind =3D label_kind::TEXT); > + > + canvas::size_t get_size () > + { > + ensure_layout (); > + return m_size; > + } > + > + void paint_to_canvas (canvas &canvas, > + canvas::coord_t offset, > + const theme &theme); > + > + void debug (const style_manager &sm); > + > + private: > + /* A particular label within an x_ruler. > + Consider e.g.: > + > + # x: 01234567890123456789012345678901234567890123456789 > + # y: 0: =E2=94=9C=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=80=E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2= =94=80=E2=94=BC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94= =80=E2=94=AC=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80=E2=94=80= =E2=94=A4 > + # 1: =E2=94=82 =E2=94=82 =E2= =94=82 > + # 2: label A label B label C > + # > + > + Then "label A" is: > + > + # m_connector_x =3D=3D 8 > + # V > + # x: 0123456789012 > + # y: 0: =E2=94=AC > + # 1: =E2=94=82 > + # 2: label A > + # x: 0123456789012 > + # ^ > + # m_text_coord.x =3D=3D 6 > + > + and m_text_coord is (2, 6). > + The y cooordinates are stored with respect to label_dir::BELOW; > + for label_dir::ABOVE we flip them when painting the ruler. */ > + class label > + { > + friend class x_ruler; > + public: > + label (const canvas::range_t &range, styled_string text, style::id_t= style_id, > + label_kind kind); > + > + bool operator< (const label &other) const; > + > + private: > + canvas::range_t m_range; > + styled_string m_text; > + style::id_t m_style_id; > + label_kind m_kind; > + canvas::rect_t m_text_rect; // includes any border > + int m_connector_x; > + }; > + > + void ensure_layout (); > + void update_layout (); > + int get_canvas_y (int rel_y) const; > + > + label_dir m_label_dir; > + std::vector