The JetBrains Blog https://blog.jetbrains.com/zh-hans Developer Tools for Professionals and Teams Wed, 14 Jun 2023 08:41:55 +0000 zh-CN hourly 1 https://blog.jetbrains.com/wp-content/uploads/2023/02/cropped-icon-512-32x32.png The JetBrains Blog https://blog.jetbrains.com/zh-hans 32 32 JetBrains 码上道:使用分布式数据库简化开发流 https://blog.jetbrains.com/zh-hans/blog/2023/06/13/jetbrains-mashangdao-with-tidb/ Tue, 13 Jun 2023 08:00:00 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/06/YouTube_1280x720.png https://blog.jetbrains.com/?post_type=blog&p=362628

在 2023 年的研发场景下,很多研发工程师对分布式数据库感兴趣。这不是因为分布式数据库的编写更加尖端,更为复杂。恰恰相反,分布式数据库可以提供出类似单体数据库的接口,让因为各种原因不愿意分库分表的研发工程师(如对架构复杂度上限有要求,或希望全局有事务时)有了更多的选择。

TiDBPingCAP 公司自主设计、研发的开源分布式关系型数据库,是一款同时支持在线事务处理与在线分析处理 (Hybrid Transactional and Analytical Processing, HTAP) 的融合型分布式数据库产品,具备水平扩容或者缩容、金融级高可用、实时 HTAP、云原生的分布式数据库、兼容 MySQL 5.7 协议和 MySQL 生态等重要特性。目标是为用户提供一站式 OLTP (Online Transactional Processing)、OLAP (Online Analytical Processing)、HTAP 解决方案。TiDB 适合高可用、强一致要求较高、数据规模较大等各种应用场景。

DataGrip 是面向开发人员的数据库管理环境,是我们为解决专业 SQL 开发者的特定需求量身定做的全新数据库 IDE,旨在查询、创建和管理数据库。数据库可以在本地、服务器或云端工作。

本次演讲将围绕着 TiDB + DataGrip 进行叙述,我们可以看到在现代架构与现代研发工具的加持下,你的开发流将顺滑到极致!忘记手动编写烦人的实体类,忘记分库分表,忘记 ETL!你值得去做更有价值的工作负载!

本次活动将在 JetBrains 中国官方 BiliBili 频道和微信视频号同步直播,别忘了预留时间,并关注 JetBrains 微信公众号以获取第一手活动信息。

主讲嘉宾

王琦智

TiDB 开发者生态高级工程师

前腾讯音乐 TME Live 后端开发,曾成功搭建 TME Live 超高并发业务集群,有丰富的应用开发经验。目前在PingCAP 负责 TiDB 开源技术生态拓展,开发者文档编写,多次集成 TiDB 与外部中间件,并输出文档,如 ProxySQL、AWS 的数个组件等。

主持人

范圣佑 (Shengyou)
圣佑是 JetBrains 技术布道师,对 JetBrians 相关技术与产品也有深入的理解。作为布道师,他乐意分享自己的开发经验,帮助更多开发者提升生产力及代码质量。

提交您的问题!

关于分布式数据库您是不是有很多的好奇与疑问呢?欢迎在评论区留下你的问题,我们将在直播时一并为大家解答!

JetBrains 码上道

JetBrains 码上道是由 JetBrains 团队打造的系列视频节目。我们将邀请到各领域的科技领袖、技术专家、社区大佬,一起来专注前沿的技术趋势,分享实用的开发经验,聚焦热门的开发话题!您喜欢的内容,码上道!

]]>
API 验证器:开启 ReSharper 插件新时代 https://blog.jetbrains.com/zh-hans/dotnet/2023/06/13/api-resharper/ Tue, 13 Jun 2023 03:00:00 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/05/rs-how-tos-blog-featured-image-1280x600-1.png https://blog.jetbrains.com/?post_type=dotnet&p=362592 ReSharper 既是插件,也是强大的插件创建平台。 设计产品的各个方面时,ReSharper 开发团队采用了依赖项注入方法。 这就使 ReSharper 的任何组件都像一个可使用插件重写的构建块,为微调 ReSharper 行为带来无限可能。 

作为插件作者,您可以利用 ReSharper 开发者自己使用的所有组件。 但这种自由度也有不利的一面。

“每个公共组件都是一个扩展点”的策略限制了我们重构现有代码的能力。 为进程外模式准备 ReSharper 或为集成到 Fleet 准备我们的代码库等大规模重构也因此变得更为困难。 多年来,我们一直表示 ReSharper 的每个新版本都与已安装的插件不兼容,即使它们引用的 API 集在技术上没有发生变化。 由于我们的内部重构,我们无法保证无缝迁移。  

ReSharper 和 Rider 每年都会发布三个主要版本,因此直到最近,插件开发者每年都要被迫重新编译插件至少三次。 我们明白,这让插件编写者的工作颇为艰难,我们也意识到必须做出改变。 

因此,我们开始考虑让 ReSharper 的平台自动检测其插件引用的 API 中的变化。  

什么是 API 验证器? 

在 ReSharper 中,我们将每个插件都视为产品的重要组成部分。 所有部件在安装期间组合。 在这个阶段,我们现在可以检测插件使用的 API 是否被更改或在最新版本的 ReSharper 中不再可用。

如果 API 验证器发出危险信号,该插件将被标记为 Incompatible(不兼容),不会添加到安装的产品中。 但是,它将保留在 ReSharper 内 Extension manager(扩展程序管理器)中的已安装插件列表中。

通过 API 验证的插件将自动迁移到产品的下一个版本。 ReSharper 2023.1 实际上是第二个包含 API 验证器的 ReSharper 版本,因此在 2023.1 版本之前兼容的所有插件都将被保留。 

指定依赖项的新方式

引入 API 验证器的同时,我们还更改了指定与产品版本兼容性相关的依赖项的要求。 这需要讲一些背景。

在内部,我们把主要版本称为“wave”。 命名 wave 时,我们取发布年份的最后两位数字,再加上一位数字表示该年的版本编号。 例如,对于 2023 年的第一个主要版本,wave 名为“wave 231”,2022 年的第三个版本是“wave 223”。 

在插件中将 wave 名称指定为依赖项有两种好处。 第一,它将帮助我们验证软件包确实是 ReSharper 的扩展程序。 第二,指定兼容 wave 的范围让您可以指示哪些版本的产品与插件兼容。 

过去,我们建议为此使用区间表示法。 例如,如果您将区间表示法的范围指定为 [223-231),则表示插件仅与 2022.3 版本兼容。 但是,引入 API 验证器后,我们现在建议不使用区间表示法指定 wave。 您可以将 231 指定为 wave 依赖项来声明向前兼容性,如下所示:

<dependencies>
<dependency id="Wave" version="231.0.0"/>
</dependencies>

Marketplace 集成

在安装期间执行的 API 验证器的代码也在 JetBrains Marketplace 中实现。 这对插件开发者来说是个好消息,因为当 SDK 中不再出现所用 API 时,插件的页面现在会显示详细警告。 在每个抢先体验预览或主要版本公开之前,会在所有可用插件上执行 API 验证。 

JetBrains Marketplace 插件网页上的 API 验证结果示例。

插件开发者还可以选择在插件未通过验证流程时选择接收电子邮件通知。 这些电子邮件详细说明了哪些 API 已过时、被移除、更改或更新,帮助您将无法编译的部分归零,无需打开包含插件代码的项目。 

为 ReSharper 或 Rider 编写插件时需要帮助? 

接下来有什么值得期待的? 

目前,JetBrains Marketplace 中可见的产品兼容性信息仅供发布者使用。 我们不可能自动下架插件,即使我们确定它已经完全过时并且肯定与当前版本的 ReSharper 不兼容。 目前,我们在 Extension manager(扩展程序管理器)中显示这些插件可用,尽管任何安装它们的尝试都会失败,因为它们无法通过嵌入式验证。

将来,我们将从动态中清除此类插件。 带有插件的动态目前基于 wave 版本构建,而不是产品的实际构建号。 这意味着 ReSharper 2023.1 EAP 1 的插件列表与发布版本相同。 Marketplace 和 ReSharper 开发团队已经着手从基于 wave 的动态切换到基于构建的动态,敬请期待更多更新。 

致谢

我们要感谢 Mike-E 的无限热情,感谢他让我们注意到实现这一功能的重要性。

本博文英文原作者:

Sue

Sasha Ivanova

]]>
https://blog.jetbrains.com/dotnet/2023/06/13/api-resharper/
欢迎参加 2023 开发者生态系统调查 https://blog.jetbrains.com/zh-hans/blog/2023/06/09/take-part-in-the-developer-ecosystem-survey-2023/ Fri, 09 Jun 2023 02:48:12 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/06/Featured_Blog_1280x720-1.png https://blog.jetbrains.com/?post_type=blog&p=360448 JetBrains 从 2017 年以来一直在开展年度开发者生态系统调查,以加深我们对开发者社区的了解、为您改进我们的产品和工具,以及创建行业年度概况。 今年也不例外!


2023 开发者生态系统调查现已开始,我们邀请您参与我们的研究。 为了对软件开发生态系统进行全面、独立的研究,我们需要您的宝贵见解和反馈。 调查大约需要 30 分钟,请与我们分享您的开发者故事。

调查以 10 种语言提供,您还将有机会赢得以下奖品之一:

  • MacBook Pro 16
  • NVIDIA GeForce RTX 4090 显卡
  • iPhone 14 Pro
  • 价值 300 美元的 Amazon 礼品卡
  • JetBrains All Products Pack

您的意见不仅将帮助我们,还将帮助整个社区,因此我们诚邀您参与调查并分享您的想法。 请点击以下链接开始:

参加开发者生态系统调查

参与的开发者越多,越能代表社区的真实情况。 与您的朋友和同事分享调查,您将获得一次额外的抽奖机会。 推荐链接位于调查的最后一页。


与往常一样,我们将分享详细的信息图表,包括调查结果以及对最新技术和软件开发趋势的见解。 此外,我们还将提供匿名化原始数据,供您自行研究。 最后,我们将为所有参与者准备个性化的信息图表,展示您与社区其他成员的对比情况。

请参与调查,帮助我们了解 2023 年开发者生态系统的真实情况。 您的心声至关重要!

此致

JetBrains 研究团队

本博文英文原作者:

Sue

Anastassiya Sichkarenko

]]>
https://blog.jetbrains.com/pt-br/blog/2023/06/09/take-part-in-the-developer-ecosystem-survey-2023/ https://blog.jetbrains.com/ko/blog/2023/06/09/take-part-in-the-developer-ecosystem-survey-2023/ https://blog.jetbrains.com/ja/blog/2023/06/09/take-part-in-the-developer-ecosystem-survey-2023/ https://blog.jetbrains.com/fr/blog/2023/06/09/take-part-in-the-developer-ecosystem-survey-2023/ https://blog.jetbrains.com/es/blog/2023/06/09/take-part-in-the-developer-ecosystem-survey-2023/ https://blog.jetbrains.com/blog/2023/06/09/take-part-in-the-developer-ecosystem-survey-2023/ https://blog.jetbrains.com/de/blog/2023/06/09/take-part-in-the-developer-ecosystem-survey-2023/
在 JetBrains Rider 中使用实时模板提高生产力 https://blog.jetbrains.com/zh-hans/dotnet/2023/06/08/jetbrains-rider-2/ Thu, 08 Jun 2023 03:00:00 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/02/rd-how-tos-blog-featured-image-1280x600-1-1.png https://blog.jetbrains.com/?post_type=dotnet&p=361136 说实话, 编程的有些部分乏味又重复。 JetBrains 产品提供通用快速修复和模板来帮助减少样板代码,使大多数开发者受益。 即便如此,您的项目可能拥有独特的代码结构和模式,只有您和您的团队使用。 

为您和您的团队创建一组专属模板在整个应用程序开发过程中使用是不是听起来不错? 使用实时模板,您就可以做到这一点

本文将展示如何使用占位符变量创建新的实时模板并与其他项目成员共享模板。

什么是实时模板?

实时模板是一个代码块,可以扩展为循环、条件、声明或语句等常用构造。 在 JetBrains IDE 的上下文中,实时模板有一个与之关联的键,使其在扩展时易于记忆和访问。

此外,实时模板分为两类:简单模板形参化模板

简单模板仅包含固定的纯文本,用于减少生成样板代码时的按键次数。 形参化模板包括的变量会启用第二个步骤,使用基于文件类型上下文的计算值替换输入字段或让您手动指定值。

所有实时模板都可以使用语言或文件扩展名等类别限制在特定上下文中。 这种灵活性确保实时模板只在您需要的时间和位置出现。

创建新的实时模板

在本例中,您将创建一个新的 rec 实时模板,用来创建 record 定义和设置名称。 此模板仅在 C# 上下文中可用。

首先,打开 JetBrains Rider 的设置,导航到 Editor | Live Templates | C#(编辑器 | 实时模板 | C#)。 在这里,您将看到所有现有的实时模板。

JetBrains Rider 中的 Editor | Live Templates | C#(编辑器 | 实时模板 | C#)设置

点击右上角现有模板列表旁的 New Template(新建模板)按钮。 您需要将 Shortcut(快捷键)的以下值更改为“rec”,将 Description(描述)的以下值更改为“创建 C# 记录”,同时将所有其他设置保留为默认值。

您还需要在左侧使用以下模板。

// Created At $DATETIME$
$MODIFIER$ record $NAME$($END$);

粘贴模板后,点击右下角的 Edit variables(编辑变量)按钮更改每个占位符的宏。 确保匹配以下顺序和设置。

占位符 可编辑 描述
$DATETIME$  未检查 采用指定格式的当前日期和时间
$MODIFIER$ 已检查 执行基本补全
$NAME$ 已检查 未选择任何宏

这些模板变量可以在实时模板中使用,并且将自动填充或充当占位符,以后您可以在使用实时模板时通过 Tab ⇥ 切换。

您会注意到 $END$ 不在变量列表中。 这是因为 $END$ 是保留变量,指示光标的最终位置。

设置中实时模板的最终形态

现在,我们可以开始使用新的实时模板了! 在 C# 文件中,输入 rec,您将看到创建的实时模板,然后可以按 Enter 开始扩展模板。 观看以下视频了解它的实际运作。

JetBrains Rider 中的实时模板

共享实时模板

实时模板可以通过 JetBrains Rider 基于层的设置与其他团队成员共享。 遵循上一节中的相同步骤,但在点击保存按钮之前,务必使用下拉菜单选择 team-shared(团队共享)选项,这会将新创建的实时模板保存到解决方案根下的 .sln.DotSettings 文件中。

JetBrains Rider 中的保存菜单和团队共享选项。

如果在 .DotSettings 文件中没有看到实时模板内容,尝试删除实时模板并重新创建。 JetBrains Rider 只会保存个人和团队共享环境之间的差异。 务必将 .DotSettings 文件签入源代码管理,以便与团队成员共享模板。

结论

实时模板是 JetBrains Rider 的一项强大功能,即使您使用过我们的模板,您也很可能尚未创建过自己的模板。 实时模板允许跨团队共享知识。 您也可以省去不必要的输入,使用它们改进在线讲座和现场演示。 我们希望这个简短的教程能让您有意愿尝试实时模板并与团队成员共享。

如果您有想分享的实时模板,请在下方评论。

参考

图片来源:Thomas Despeyroux

本博文英文原作者:

Sue

Khalid Abuhakmeh

]]>
https://blog.jetbrains.com/ja/dotnet/2023/06/08/jetbrains-rider-2/ https://blog.jetbrains.com/dotnet/2023/06/08/jetbrains-rider-2/
database/sql 软件包使用入门 https://blog.jetbrains.com/zh-hans/go/2023/06/07/database-sql/ Wed, 07 Jun 2023 14:38:15 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/02/Blog_Featured_image_2560x1200-1.png https://blog.jetbrains.com/?post_type=go&p=361133 本文由外部贡献者撰写。

Damaso Sanoja

Damaso Sanoja

Damaso Sanoja 热衷于帮助他人通过数据驱动型决策达成目标。 这促使他撰写了大量文章,内容覆盖最流行的关系数据库、客户关系管理系统、企业资源规划系统、主数据管理工具,以及最近用于机器学习和 AI 项目的数据仓库系统。 这份对数据管理的执着可以归结于他的第一台电脑是没有软盘的 Commodore 64。

GitHub

 

 

 

 

database/sql 软件包是一个标准库,提供与 SQL 数据库交互的接口。 该软件包使应用程序能够查询和更新数据库,并提供可用于各种 SQL 数据库的可移植接口。 database/sql 软件包通常与数据库驱动程序结合使用,后者提供了一个具体的接口实现,允许您在方便的方法后抽象数据库选项。

本实践教程将展示如何开始使用 database/sql 软件包,我将指导您如何完成以下工作:

  • 创建 GoLand 项目。
  • 设置数据库。
  • 使用 Go 和 GoLand 实现基本查询和事务。
  • 如何构造错误消息。

database/sql 软件包使用入门

使用 Go 编写的数据库很易于上手。 但是,您的本地机器必须满足某些先决条件。

前提

在开始之前,您应该确保已经安装了所有必要工具并拥有完成本教程所需的适当资源:

  • Go:如果还没有,请在本地机器上下载并安装 Go
  • MySQL 服务器:Go 支持流行的关系数据库管理系统,例如 MySQL、Oracle、Postgres、SQL Server、SQLite 等。 但是,在本教程中,您将使用 MySQL。 在这里下载适用于您的操作系统的 MySQL Community Server。 有关设置 MySQL 的说明,请参阅 MySQL 使用入门
    • 重要说明:根据您选择的安装方法,MySQL 服务器可能会自动启动并在后台运行,直到您将其停止。 同样,安装服务后,MySQL 服务可能需要手动启动。
  • MySQL 数据库:您需要一个可以使用的空数据库。 本教程将数据库称为 recordings,但您可以使用任意名称。 您可以在找到有关在 MySQL 中创建数据库的信息。
  • GoLand IDE:您可以在这里下载适用于 macOS、Linux 或 Windows 的 GoLand。
  • 教程仓库:您可以将包含教程所用代码的仓库克隆到适当位置。

满足先决条件后,您就可以启动 GoLand 并开始您的第一个项目了。

在 GoLand 中创建新项目

第一步是在 GoLand 中创建新项目。 为此,在 Welcome(欢迎)屏幕上按 New Project(新建项目)按钮。

接下来,屏幕将类似于:

注意屏幕上的几个字段:

  • Location(位置)。 使用此字段为项目选择合适的位置。 但是,在本教程中,您必须选择仓库的位置。
  • GOROOT。 如果您已将 Go 安装在操作系统的默认位置,则此字段会被预填。 如果没有,请选择正确的 Go 根位置。
  • Environment(环境)。 这是一个可选字段,允许您声明环境变量,例如 GOPROXYGOPRIVATE。 在本教程中,您将使用此字段声明后续连接数据库所需的 MySQL 环境变量 DBUSERDBPASS。 为此,您必须输入相应的键值对,如下所示。

务必输入正确的值。 本教程使用以下值:

创建项目后,GoLand 会自动创建 go.mod 文件。

配置 MySQL 数据源

在 GoLand 中管理数据库非常简单,您只需要确保数据源配置正确。

要配置数据源,首先,点击 GoLand 右侧菜单中的数据库图标,然后点击加号 (+) 或配置图标,接着从可用数据库列表中选择 MySQL。

在下一个屏幕上,输入用户名和密码以连接到 MySQL。 接下来,输入数据库的名称,然后点击 Test Connection(测试连接)。

如果您之前没有配置过 MySQL,您可能会收到一条错误消息。 在这种情况下,点击 Download Driver Files(下载驱动程序文件),GoLand 将完成其余的工作。

成功测试连接后,点击 OK(确定)按钮继续。

在下一个屏幕上,将自动出现 MySQL 控制台,以及建议配置 SQL 方言的通知。 您可以将 MySQL 设置为默认方言,也可以点击 configure(配置)以选择其他方言。

现在,数据源已设置完成,您可以从 MySQL 控制台运行命令。 但是,如果您有 SQL 脚本(如本教程中所示),则不必这样做。

使用 SQL 脚本设置数据库表

您可以使用 GoLand 轻松运行 SQL 脚本。 为此,只需右键点击相应的文件并从下拉列表中选择 Run(运行)。 尝试运行 table-01.sql 脚本。 执行脚本之前,您必须为其指定一个数据目标,在本例中为 recordings 数据库。 按下 Run(运行)按钮时,脚本的输出将自动显示。

显示的脚本应如下所示:

DROP TABLE IF EXISTS album;
CREATE TABLE album (
                       id         INT AUTO_INCREMENT NOT NULL,
                       title      VARCHAR(128) NOT NULL,
                       artist     VARCHAR(255) NOT NULL,
                       price      DECIMAL(5,2) NOT NULL,
                       quantity   INT UNSIGNED,
                       PRIMARY KEY (`id`)
);


INSERT INTO album
(title, artist, price, quantity)
VALUES
    ('Blue Train', 'John Coltrane', 56.99, 5),
    ('Giant Steps', 'John Coltrane', 63.99, 62),
    ('Jeru', 'Gerry Mulligan', 17.99, 0),
    ('Sarah Vaughan', 'Sarah Vaughan', 34.98, 127);

在上面的 SQL 脚本中:

  • 第一行会删除专辑表。 这使脚本更容易根据需要多次运行,同时避免了重新创建现有表时可能出现的错误。
  • 第 2 行到第 7 行与专辑表以及相应字段的创建有关。
  • 第 8 行将 id 设为主键。
  • 脚本的其余部分使用 INSERT 语句以虚拟数据填充表。

接下来,重复这一过程来运行第二个 SQL 脚本 table-02.sql

下面的代码对应于第二个 SQL 脚本:

DROP TABLE IF EXISTS album_trx;
CREATE TABLE album_trx (
                             trx_id    INT AUTO_INCREMENT NOT NULL,
                             trx_check     INT UNSIGNED,
                             PRIMARY KEY (`trx_id`)
);

正如您所看到的,这是一个更简单的脚本,它创建的表只有 2 个字段。 您将在事务部分使用此表。

至此,您已经将 GoLand 配置为与本地 MySQL 服务关联,因此,您可以使用 SQL 脚本轻松创建和预填充表。 同样,在下一部分中,您将了解在 GoLand 中使用 database/sql 软件包有多轻松。

使用 database/sql 连接到数据库

database/sql 软件包使得使用 Go 管理数据库变得非常简单,如 connection.go 代码所示:

package main
import (
    "database/sql"
    "fmt"
    "github.com/go-sql-driver/mysql"
    "log"
    "os"
)
var db *sql.DB
type Album struct {
    ID       int64
    Title    string
    Artist   string
    Price    float32
    Quantity int64
}
func main() {
    // Capture connection properties.
    cfg := mysql.Config{
        User:   os.Getenv("DBUSER"),
        Passwd: os.Getenv("DBPASS"),
        Net:    "tcp",
        Addr:   "127.0.0.1:3306",
        DBName: "recordings",
    }
    // Get a database handle.
    var err error
    db, err = sql.Open("mysql", cfg.FormatDSN())
    if err != nil {
        log.Fatal(err)
    }
    pingErr := db.Ping()
    if pingErr != nil {
        log.Fatal(pingErr)
    }
    fmt.Println("Connected!")
}

我们来检查代码。 首先,导入必要的软件包:

  • 如前所述,database/sql 提供围绕 SQL(或类 SQL)数据库的泛型接口
  • fmt使用类似于 C 语言的 printf 和 scanf 的函数实现格式化 I/O 的软件包。
  • github.com/go-sql-driver/mysql – MySQL 的 database/sql 的实现(数据库驱动程序)。
  • log – 提供日志记录功能的标准库。
  • os为操作系统功能提供独立于平台的接口的软件包。

*var db sql.DB – 这一行是键。 db 变量使用充当数据库连接池的 sql.DB 结构声明。 如前言中所述,这使您可以方便地抽象数据库选项。

type Album struct {...} – 这是 Album 表结构的定义,它将保存数据库的行数据。

func main() – 此函数利用上方声明的 sql.DB 结构以及 database/sql 软件包和 MySQL 驱动程序。

连接数据使用 DSN(数据源名称)格式捕获。 语法:

db, err := sql.Open("mysql", "user:password@/dbname")

为简化代码,声明收集连接属性的 cfg 变量(包括之前在 GoLand 中配置的环境),后续如上所示将其用于使用 DSN 格式获取数据库句柄。 func main 的最后一部分处理可能的错误,我们将在后面的部分讨论。

运行此文件非常简单,只需点击右键并从下拉列表中选择 Run ‘go build connection.go’(运行 ‘go build connection.go’)。

与预期相同,输出显示消息 Connected!(已连接!)。

 

现在,您已连接到数据库,可以开始执行插入新行或查询行等操作了。

插入新行

我们来检查 insert.go 的代码,它插入了一个新行:

package main
import (
    "database/sql"
    "fmt"
    "github.com/go-sql-driver/mysql"
    "log"
    "os"
)
var db *sql.DB
type Album struct {
    ID       int64
    Title    string
    Artist   string
    Price    float32
    Quantity int64
}
func main() {
    // Capture connection properties.
    cfg := mysql.Config{
        User:   os.Getenv("DBUSER"),
        Passwd: os.Getenv("DBPASS"),
        Net:    "tcp",
        Addr:   "127.0.0.1:3306",
        DBName: "recordings",
    }
    // Get a database handle.
    var err error
    db, err = sql.Open("mysql", cfg.FormatDSN())
    if err != nil {
        log.Fatal(err)
    }
    pingErr := db.Ping()
    if pingErr != nil {
        log.Fatal(pingErr)
    }
    fmt.Println("Connected!")
    albID, err := addAlbum(Album{
        Title:    "The Modern Sound of Betty Carter",
        Artist:   "Betty Carter",
        Price:    49.99,
        Quantity: 10,
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("ID of added album: %vn", albID)
}
// addAlbum adds the specified album to the database,
// returning the album ID of the new entry
func addAlbum(alb Album) (int64, error) {
    result, err := db.Exec("INSERT INTO album (title, artist, price, quantity) VALUES (?, ?, ?, ?)", alb.Title, alb.Artist, alb.Price, alb.Quantity)
    if err != nil {
        return 0, fmt.Errorf("addAlbum: %v", err)
    }
    id, err := result.LastInsertId()
    if err != nil {
        return 0, fmt.Errorf("addAlbum: %v", err)
    }
    return id, nil
}

此代码块与前面的代码块非常相似,不同之处在于它向 album 表插入了一个新行。 右键点击文件并选择 Run ‘go build insert.go’(运行 ‘go build insert.go’)运行代码。

正如预期的那样,输出为:

Connected!
ID of added album: 5

正如您所看到的,使用 database/sql 管理数据库连接非常简单。 定义一个数据库句柄,然后就可以使用 SQL 命令、函数和条件语句来管理数据库。 查询数据库中的一行来巩固所学的知识。

查询单行

和前面一样,我们先检查查询单行的 single.go 的代码:

package main
import (
    "database/sql"
    "fmt"
    "github.com/go-sql-driver/mysql"
    "log"
    "os"
)
var db *sql.DB
type Album struct {
    ID       int64
    Title    string
    Artist   string
    Price    float32
    Quantity int64
}
func main() {
    // Capture connection properties.
    cfg := mysql.Config{
        User:   os.Getenv("DBUSER"),
        Passwd: os.Getenv("DBPASS"),
        Net:    "tcp",
        Addr:   "127.0.0.1:3306",
        DBName: "recordings",
    }
    // Get a database handle.
    var err error
    db, err = sql.Open("mysql", cfg.FormatDSN())
    if err != nil {
        log.Fatal(err)
    }
    pingErr := db.Ping()
    if pingErr != nil {
        log.Fatal(pingErr)
    }
    fmt.Println("Connected!")
    // Hard-code ID 2 here to test the query.
    alb, err := albumByID(2)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Album found: %vn", alb)
}
// albumByID queries for the album with the specified ID.
func albumByID(id int64) (Album, error) {
    // An album to hold data from the returned row.
    var alb Album
    row := db.QueryRow("SELECT * FROM album WHERE id = ?", id)
    if err := row.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price, &alb.Quantity); err != nil {
        if err == sql.ErrNoRows {
            return alb, fmt.Errorf("albumsById %d: no such album", id)
        }
        return alb, fmt.Errorf("albumsById %d: %v", id, err)
    }
    return alb, nil
}

这里有几个代码块值得强调。

首先,第 44 行以注释 // Hard-code ID 2 here to test the query. 作为其标题,调用 albumByID(2) 查询哪个专辑对应 ID 号 2。

第二个块对应于 albumByID 函数。 此函数使用 SELECT * FROM album WHERE id = ? 查询具有特定 ID 的专辑的数据。 如果 ID 不存在,则使用 if 条件显示错误消息。

运行代码以验证一切都按预期运作。

输出应如下所示:

Connected!
Album found: {2 Giant Steps John Coltrane 63.99 62}

查询多行

要了解如何使用 database/sql 在 Go 中查询多行,您将使用 multiple.go。 查询多行背后的逻辑类似于查询单行。

albumsByArtist 函数会查找其艺术家在 main 函数中硬编码的所有专辑。 然后,如果有匹配项,它会在输出中打印,如果没有,它会打印一个空字符串。

右键点击文件并选择 Run ‘go build multiple.go’(运行 ‘go build multiple.go’)运行 multiple.go

输出应为:

Connected!
Albums found: [{1 Blue Train John Coltrane 56.99 5} {2 Giant Steps John Coltrane 63.99 62}]

您已经学会了如何执行单行和多行查询,现在该执行更高级的操作(例如预备语句)了。

使用预备语句

根据 Go 文档:

“预备语句是由 DBMS 解析和保存的 SQL,通常包含占位符,但没有实际形参值。 后续,可以使用一组形参值执行该语句。”

在实践中,预备语句有助于避免每次代码执行相同的数据库操作时都重新创建语句,从而提高性能。

如果您有兴趣了解预备语句,请查看 prepared.go 文件。

package main
import (
    "database/sql"
    "fmt"
    "github.com/go-sql-driver/mysql"
    "log"
    "os"
)
var db *sql.DB
type Album struct {
    ID       int64
    Title    string
    Artist   string
    Price    float32
    Quantity int64
}
func main() {
    // Capture connection properties.
    cfg := mysql.Config{
        User:   os.Getenv("DBUSER"),
        Passwd: os.Getenv("DBPASS"),
        Net:    "tcp",
        Addr:   "127.0.0.1:3306",
        DBName: "recordings",
    }
    // Get a database handle.
    var err error
    db, err = sql.Open("mysql", cfg.FormatDSN())
    if err != nil {
        log.Fatal(err)
    }
    pingErr := db.Ping()
    if pingErr != nil {
        log.Fatal(pingErr)
    }
    fmt.Println("Connected!")
    // Hard-code ID 2 here to test the query.
    Album, err := albumByID(2)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Album found: %vn", Album)
}
// AlbumByID retrieves the specified album.
func albumByID(id int) (Album, error) {
    // Define a prepared statement. You'd typically define the statement
    // elsewhere and save it for use in functions such as this one.
    stmt, err := db.Prepare("SELECT * FROM album WHERE id = ?")
    if err != nil {
        log.Fatal(err)
    }
    var album Album
    // Execute the prepared statement, passing in an id value for the
    // parameter whose placeholder is ?
    err = stmt.QueryRow(id).Scan(&album.ID, &album.Title, &album.Artist, &album.Price, &album.Quantity)
    if err != nil {
        if err == sql.ErrNoRows {
            // Handle the case of no rows returned.
        }
        return album, err
    }
    return album, nil
}

正如您所看到的,该代码与用于查询单行的代码非常相似。 不同之处在于 albumByID 函数现在使用预备语句。 函数的第一部分定义使用 sql.Stmt 准备 SQL 语句的预备语句 (db.Prepare),然后调用时在第二部分中执行它 (stmt.QueryRow)。

运行代码以检查结果:

结果与前面的示例(查询单行)相同,但实际上,节省的资源在处理具有数千行的数据库时会产生显著影响。 要详细了解预备语句,请阅读此处的文档。

处理事务

事务允许您执行各种操作,例如插入、更新、查询或删除表行。 事务之所以有用,是因为它们能够将多个操作(查询)组合在一起,仅在它们都满足指定条件时才执行。 否则,不执行任何操作。

为了理解这个概念,我们来检查 transaction.go

package main
import (
    "context"
    "database/sql"
    "fmt"
    "github.com/go-sql-driver/mysql"
    "log"
    "os"
)
var db *sql.DB
type Album struct {
    ID       int64
    Title    string
    Artist   string
    Price    float32
    Quantity int64
}
type Album_trx struct {
    TRX_ID    int64
    TRX_CHECK int64
}
func main() {
    // Capture connection properties.
    cfg := mysql.Config{
        User:   os.Getenv("DBUSER"),
        Passwd: os.Getenv("DBPASS"),
        Net:    "tcp",
        Addr:   "127.0.0.1:3306",
        DBName: "recordings",
    }
    // Get a database handle.
    var err error
    db, err = sql.Open("mysql", cfg.FormatDSN())
    if err != nil {
        log.Fatal(err)
    }
    pingErr := db.Ping()
    if pingErr != nil {
        log.Fatal(pingErr)
    }
    fmt.Println("Connected!")
    // Start the transaction
    ctx := context.Background()
    tx, err := db.BeginTx(ctx, nil)
    if err != nil {
        log.Fatal(err)
    }
    // First query
    _, err = tx.ExecContext(ctx, "INSERT INTO album (title, artist, price, quantity) VALUES ('Master of Puppets', 'Metallica', '49', '1')")
    if err != nil {
        tx.Rollback()
        return
    }
    // Second query
    _, err = tx.ExecContext(ctx, "INSERT INTO album_trx (trx_check) VALUES (-1)")
    if err != nil {
        tx.Rollback()
        fmt.Println("Transaction declined")
        return
    }
    // If no errors, commit the transaction
    err = tx.Commit()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("Transaction accepted!")
}

该结构类似于本教程中讨论的结构,除了最后一部分,它包含 2 个查询。 根据文档,您可以使用代表事务的 sql.Tx 执行数据库事务

让我们从注释 // Start the transaction 开始检查代码,了解这些事务的运作方式。 此代码创建一个上下文 (ctx),用于使用 DB.BeginTx 方法启动事务。 接下来,是 2 个使用 tx.ExecContext 方法的查询。

如果事务成功,第一个查询只在专辑表中插入一个新行。 请注意,如果出现错误,tx.Rollback() 方法会阻止操作执行。

在本例中,第二个查询用于显示事务背后的逻辑。 请注意,在本例中,它还使用 INSERT 语句向 album_trx 控制表添加新行。 但是,该值为负,并且由于字段 trx_check 不接受负值(它被定义为 INT UNSIGNED),该查询将不会进行。

因此,条件将触发 tx.Rollback() 方法,终端将显示 Transaction declined 错误消息。 而且,无论第一个查询是否顺利通过,都不会更新任何表。

运行代码以检查事务的行为。

与预期相同,事务被拒绝。

现在,编辑 _, err = tx.ExecContext(ctx, "INSERT INTO album_trx (trx_check) VALUES (-1)") 行,将值 -1 更改为 1,然后再次运行代码。

点击数据库图标,展开数据库表,然后双击每个表名,即可轻松检查表是否被修改。

本教程中展示的相同原理可用于创建具有同时影响多个表的多项检查的复杂操作。 一次查询失败就足以导致事务中断。

在 Go 中处理错误

许多原因可能导致错误。 例如,如果您尝试使用尚未声明的变量、尝试除以 0 或存在拼写错误,就可能会出错。 幸运的是,要处理 Go 中的代码错误,您可以使用内置的错误处理函数。

这些函数已在本教程中广泛使用,只是没有详细说明。 我们来看一下获取 MySQL 数据库句柄时错误消息是如何管理的:

 // Get a database handle.
    var err error
    db, err = sql.Open("mysql", cfg.FormatDSN())
    if err != nil {
        log.Fatal(err)
    }
    pingErr := db.Ping()
    if pingErr != nil {
        log.Fatal(pingErr)
    }
    fmt.Println("Connected!")

在这段代码中:

  • 第一行声明变量 errerror 并将其初始化为零。
  • 第二行使用 database/sql 软件包通过 Go MySQL Driver 连接到数据库。
  • 在第三行中,使用了一个条件。 如果身份验证期间发生错误(布尔值不为 null),则使用 log.Fatal(err) 存储日志。 相反,如果没有错误,则执行下一个代码块。
  • 最后一段代码使用 Ping() 测试与数据库的连接。 如果发生错误,log.Fatal(pingErr) 会将其存储在日志中,但如果没有,将使用 fmt.Println("Connected!") 打印成功消息。

正如您所看到的,使用 fmtlog 等软件包构建错误消息非常简单。

您可以像在代码中构建错误消息一样轻松地使用 GoLand 调试代码中的问题。 您可以打开任意教程文件来查找错误消息示例。

结论

本教程解释了如何实现 database/sql 软件包以在 Go 中访问数据库、执行不同类型的查询、插入数据,以及执行事务。 此外,我们还简要回顾了 Go 中的错误处理。 在教程中,您已经了解到 GoLand 在编写和运行代码方面有多么方便。 您可以在这里详细了解 JetBrains 为开发者提供的工具。

本博文英文原作者:

Sue

Sergey Kozlovskiy

]]>
https://blog.jetbrains.com/go/2023/06/07/database-sql/
中国大陆业务将落地至 JetBrains 上海公司 https://blog.jetbrains.com/zh-hans/blog/2023/06/05/jetbrains-shanghai-office/ Mon, 05 Jun 2023 00:00:00 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/06/Blog_1280x720-2x.png https://blog.jetbrains.com/?post_type=blog&p=359540

感谢您长期以来对 JetBrains 产品的支持与喜爱。

自 2021 年 JetBrains 上海公司成立以来,我们一直致力于实现完全本地化的商业运作模式。在此,我们想与您分享在不久的将来 JetBrains 上海公司能为您带来的主要好处:

  • 从 2023 年 9 月 4 日起,JetBrains 上海公司将负责所有产品在中国大陆的直接销售工作。这将使我们能够为在中国大陆的直采客户1开具增值税发票。
  • 为所有产品提供稳定价格2,产品定价将从美元不含税价转为含增值税的人民币价格。
  • JetBrains 的在线商店将支持中国大陆本地的支付方式,如支付宝、微信支付等。此外,商业客户还可使用国内电汇支付方式。

*1: 简而言之,所有通过 JetBrains 的在线商店和 JetBrains Account 平台进行购买的客户都是直采客户。 

*2: 我们希望推出长期且稳定的人民币价格,以便我们的客户能够更轻松地制定他们的预算。尽管汇率会有一定的波动,我们将会尽力提供尽可能长期稳定的人民币价格,如因故需调整定价,我们会提前通知。

这将对我们的客户带来什么影响?

这些变化将适用于所有向 JetBrains 直接采购的中国大陆客户,通过 JetBrains 合作伙伴进行采购的客户并不会受到影响。这意味着从人民币定价生效日期起,适用于中国大陆的JetBrains 官网、在线商店、JetBrains Account 平台上的产品单价将以新的人民币价格显示,中国大陆客户通过这些平台操作的订单和报价单会由 JetBrains 上海公司处理。中国大陆客户在生效日期前获得的官方美元报价将全数失效,您需重新通过 JetBrains 上海公司获得新的官方人民币报价单。

另外,自人民币定价生效之日起,自动续费选项将暂时无法使用。我们正在努力为我们的客户寻找最佳的解决方案,敬请期待。如果您原先有开启自动续费设定,在该生效日之后请登录您的 JetBrains 账户用上面提到的任何一种可用的付款方式手动处理你的付款。

我们理解一些商业客户需要额外的准备工作才能将 JetBrains 上海公司列为供应商。如果您需要更多的信息来协助贵公司在内部系统中为 JetBrains 上海公司创建一个供应商档案,请随时发邮件给我们的上海销售团队寻求帮助支持。

我们希望,随着本地业务的不断完善,JetBrains 中国将能够为您提供更多便捷和周到的服务。与此同时,如果您有任何问题,请随时通过 sales.cn@jetbrains.com 或者我们的微信官方账号(@JetBrains中国)与我们取得联系,我们将很乐意帮助您。

JetBrains 团队

JetBrains Shanghai

捷并思(上海)信息技术有限责任公司

A51. 35/F, PingAn Riverfront Financial Center, 757 Mengzi Road, Huangpu District, Shanghai 200023, China

上海市黄浦区蒙自路757号平安滨江金融中心35层A51

]]>
为什么不用 Go 编写操作系统? https://blog.jetbrains.com/zh-hans/go/2023/06/01/go/ Thu, 01 Jun 2023 03:00:00 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/05/DSGN-16534-Blog-Featured-image-1280x600-2x.png https://blog.jetbrains.com/?post_type=go&p=358588 本文由外部贡献者撰写。

Aniket Bhattacharyea

热爱计算机和软件的数学研究生。

网站

操作系统是计算机系统的心脏和灵魂。 操作系统管理计算机的硬件和软件资源,并为用户提供与计算机交互的手段。 传统上,C 语言和汇编等语言因其低开销和“接近机器”的特性而被用于编写操作系统。 但是,越发流行的 Go 等高级语言新引入了一些特性,可以使操作系统等复杂软件更易开发。 例如,类型安全、错误处理和并发等特性有利于操作系统编写。 因此,Go 这样的高级语言应该会成为操作系统开发的自然选择。 所以,为什么不是呢?

在本文中,您将了解为什么 C 语言这样的语言在操作系统开发中占有重要地位,以及使用 Go 编写操作系统是否可行。

为什么操作系统或内核是使用 C 语言和汇编编写的?

用于编写特定操作系统的语言可能难以确定,因为每个操作系统都是使用多种语言的组合编写而成。

操作系统的组件具有不同职责,可能使用不同的语言编写。 操作系统的核心组件是负责与硬件交互的内核,内核几乎从来都是使用 C 语言或汇编语言编写。 面向用户的组件,例如 GUI 应用,可以使用任何语言编写。 举例来说,Android 将 Java 用于用户空间组件,比如 GUI 框架和“相机”、“电话”等系统应用。 相比之下,内核使用的是 C 语言和汇编,而低级系统组件(例如库)使用的是 C++。

本文特别关注 Go 是否适合编写操作系统内核。 几乎所有的主流内核都使用 C 语言编写,中间夹杂着一些汇编。 C 语言统治内核领域的几个原因:

  1. 直接内存管理:C 语言的卖点是它允许程序员直接处理内存。 虽然在编写内核时手动内存管理通常具有挑战性,但您会想尽可能多地控制内存,而 C 语言恰恰可以满足这样的需求。 内存映射 I/ODMA 控制器、页表、交换等概念都需要内存操作,使用 C 语言可以实现。
  2. 没有抽象:C 语言没有哈希、树或链表这样的复杂数据结构,程序员可以根据需要自己实现。 这提供了对代码更精细的控制,因为程序员可以调整实现的详细信息来提高效率。
  3. 不需要运行时:与 Java 和 Python 不同,C 语言不需要运行时。 只需要一个调用 main() 函数的机制,C 语言就能够顺畅运行。 这意味着您可以直接在硬件上运行 C 语言程序,而不必纠结于内存管理、进程管理等问题。
  4. 可移植性:C 语言已被移植到多种 CPU 架构,是编写支持多架构内核的绝佳选择。

不过,C 语言本身通常不足以编写整个内核,某些情况下您需要编写汇编代码:

  1. 手动编写的汇编可能比编译器生成的更好。 某些操作可能难以在 C 语言中实现,并且编译器生成的机器码可能较为复杂或效率低下。 在这种情况下,手动编写汇编是更好的选择。
  2. 某些代码无法使用 C 语言编写。例如,在 C 语言中切换任务时,无法将寄存器保存到堆栈,也不能将堆栈指针保存到任务控制块,因为 C 语言不提供对堆栈指针的直接访问。

为什么 Go 可以成为操作系统开发的替代语言?

Go 这样的高级语言提供了一些优良特性,从表面上看,它们似乎是操作系统开发的绝佳选择:

  1. 某些类型的 bug 在高级语言中不太可能出现。 缓冲区溢出use-after-free bug 在 Go 等语言中几乎是不可能的。 即使是由专业程序员精心编写的 C 语言代码也会无意中包含这样的 bug。 use-after-free bug 十分常见,因此 Linux 内核包含内存检查器,用于在运行时检测一些 use-after-free 和缓冲区溢出 bug。
  2. 在高级语言中并发更容易处理,因为几乎每种高级语言都内置了处理并发所需的机制。
  3. Go 这样的语言的类型安全可以防止 C 语言的宽松类型强制

为什么 Go 没有用于操作系统/内核开发?

虽然 Go 提供了优良的特性,可以让操作系统开发者的工作更加轻松,但它也有一些局限性。

作为具有垃圾回收的语言,Go 并不真正适合操作系统开发。 使用 Go 编写内核意味着仔细编写代码,最大程度减少堆使用来小心绕过 Go 的垃圾回收。 如这篇 Reddit 帖子中所述,鼠标延迟可能是因为中断处理程序分配了触发垃圾回收的内存。

Go 还需要大量运行时才能执行,不能直接在硬件上运行。 尽管 TinyGo 可以将 Go 编译为在裸机上运行,但与 C 语言相比,它只支持少量的架构,而 C 语言几乎可以在任何架构上运行。

另一个相关问题是系统调用构成了典型 Go 运行时中的大量操作。 运行时为各种操作与操作系统通信,例如写入文件或启动线程。 但是,编写操作系统时,您必须在裸机上实现这些操作,并修改 Go 运行时来调用实现。 然后问题归结为,您是否真的想花这么多时间和精力来修改运行时,毕竟 C 语言等其他语言允许您立即开始编写操作系统。

正如您所看到的,使用 Go 编写操作系统并非不可能。 不过,编写可供普通用户使用的非小型操作系统几乎不可能。 编写一个在单一架构上启动并进入 shell 的操作系统很容易,但是使用 Go 编写一个在多种架构上运行、支持不同设备(如显卡或网卡)并且可能符合 POSIX 的操作系统将非常具有挑战性。 当然一些特性可以省略,但这会限制操作系统的可行性。

使用 Go 编写的操作系统

虽然 Go 不是最适合操作系统开发的选择,但这并不意味着使用 Go 编写操作系统是不可能的,许多研究项目正在探索如何做到这一点。

Biscuit 是使用 Go 编写的操作系统,在 64 位 X86 架构上运行。 它使用经过修改的 Go 1.10 运行时实现,其中添加了更多汇编代码来处理系统调用和中断处理程序的引导和进入/退出。 使用汇编编写的引导块加载 Go 运行时和“shim”层。 Go 运行时期望与底层内核通信来提供各种功能。 由于没有底层内核,“shim”层提供了这些功能。

Biscuit 为用户进程提供 POSIX 接口,支持 fork、exec 等。 它实现了支持核心 POSIX 文件系统调用的文件系统。 Biscuit 为使用 Go 编写的 Intel PCI-Express 以太网 NIC 实现了 TCP/IP 堆栈和驱动程序。 使用 POSIX 接口,Biscuit 可以在不修改源代码的情况下运行许多 Linux C 程序。

不过,Biscuit 缺少许多功能,例如调度优先级、换出页面或磁盘,以及许多安全功能,例如用户、访问控制列表和地址空间随机化。

有关 Biscuit 的更多详细信息,请参阅白皮书

gopher-os 是另一个使用 Go 编写的概念验证内核。 与 Biscuit 一样,它使用汇编来设置 Go 运行时和加载内核。 不过,它仍处于开发的早期阶段,自 2018 年以来没有获得任何更新。 它在最新版本的 Go 上也无法运作。

Clive 是另一个使用 Go 编写的单内核操作系统,但它不在裸机上运行,并且使用经过修改的 Go 编译器来编译 Clive 软件。

gVisor 是使用 Go 编写的应用程序内核,它在沙盒容器中实现 Linux 系统 API。

结论

虽然 C 语言在操作系统开发中占主导地位,但 Go 提供了类型安全、自动内存管理和并发等特性,有潜力成为操作系统开发的绝佳选择。 不过,由于缺乏对运行时和语言不同方面的微调控制,以及 C 语言的受欢迎程度,Go 很难在操作系统开发领域站稳脚跟。 但是,一些研究操作系统已经使用 Go 编写,我们可以期待在不久的将来使用 Go 编写消费者友好型操作系统。

本博文英文原作者:

Sue

Sergey Kozlovskiy

]]>
https://blog.jetbrains.com/go/2023/06/01/go/
如何在 Space 中使用 Dart 和 Flutter 软件包仓库 https://blog.jetbrains.com/zh-hans/space/2023/05/31/space-dart-flutter/ Wed, 31 May 2023 08:47:36 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/05/blog-post-2560x1200-1.png https://blog.jetbrains.com/?post_type=space&p=358091 Space 中的 Dart 和 Flutter 软件包仓库

您担心代码的安全性吗? 不想再为 Dart 软件包依赖外部源代码?

JetBrains Space 为私有 Dart 软件包存储提供了全面解决方案。 使用它,您可以安全地存储和管理代码和软件包依赖项,并让团队在组织内快速访问和共享软件包。

无论是处理移动、控制台还是 Flutter 项目,Space Packages 都能让您轻松存储和共享 Dart 软件包。

阅读本文,了解如何将 Space Packages 用于 Dart 或 Flutter 项目。

为什么要为 Dart 使用 Space Packages?

虽然目前全球只有 9% 的开发者使用 Dart,但在 2022 开发者生态系统现状调查中,46% 的受访者证实,基于 Dart 构建的跨平台移动框架 Flutter 非常受欢迎。

在 Space 中,我们明白支持流行软件包类型的重要性,并且已经向支持的软件包列表添加了 Dart,以及常用类型,包括容器、Maven、NuGet、Python、Composer、npm,甚至任意文件。

在完整开发平台中使用 Space Packages 的好处:

  • 易于设置:您可以创建私有仓库,或从 GitHub、GitLab 或 Artifactory 轻松迁移
  • 将软件包集成到管道:使用一致的界面、快速导航和统一的权限系统,将软件包存储在源代码和 Automation 构建旁边。
  • 更稳定的构建:受益于不可变版本、易于配置的 Gradle 构建缓存和缓存不可靠的仓库。
  • 任何负载的软件包共享:Space Packages 适用于小型团队和大型企业。 即使工作量很大,也能享受合理的工作时间。

免费开始

存储内部 Dart 软件包依赖项

您可以使用 Space 中的私有 Dart 仓库安全存储和共享自定义软件包,使代码仅供团队成员访问。

  1. 在 Space 的 Dart 项目中,创建私有 Dart 仓库。 阅读本指南了解详情。
在 JetBrains Space 中创建 Dart 仓库
  1. 设置 Space Automation 作业,以将软件包发布到私有仓库。 在这篇指南中获得更多详细信息。
job("Build, run tests, and publish") {
    container(displayName = "Run script", image = "dart:stable") {
        shellScript {
            content = """
                dart compile exe .bindart_package.dart
                dart test .testdart_package_test.dart
                dart pub token add https://dart.pkg.jetbrains.space/acme-corp/p/my-project/my-dart-repo --env-var JB_SPACE_CLIENT_TOKEN
                dart pub publish -f
            """
        }
    }
}
  1. 您现在可以在 Dart 项目中引用来自这个私有仓库的软件包。
Space 中的私有 Dart 软件包仓库

公开分发 Dart 软件包

要公开分发内部存储的软件包,请创建一个额外的公共仓库。 为此,您可以使用另一个 Space 仓库作为发布的目标仓库。

  1. 创建一个公共 Dart 仓库,并选择 Public access(公开访问)。 这将是我们的目标仓库。
公开分发 Dart 软件包
  1. 现在,需要为新创建的目标仓库创建访问令牌。 将其打开,点击 Get Started(使用入门)。 您将看到一个帮助窗口,其中包含有关如何连接到此仓库的提示。
  2. 切换到 Publish(发布),点击 Generate write token(生成写入令牌)。 将令牌复制到剪贴板。
发布软件包仓库
  1. 打开源仓库的 Settings(设置)页面。
  2. 转到 Remote Repositories(远程仓库)标签页,在这里添加目标仓库。
远程仓库
  1. 对于 Authentication(身份验证),选择 Token(令牌)并粘贴第 3 步中的令牌。
创建新的远程仓库
  1. 需要将软件包发布到目标公共仓库时,转到源仓库,选择软件包,然后点击 Publish to remote repository(发布到远程仓库)。
发布远程仓库
  1. 就是这些! 软件包现在已位于公共仓库中。

发布 Flutter 软件包

同样的使用场景也适用于 Flutter 软件包 – 它们可以和普通的 Dart 软件包存放在同一个 Dart 仓库中。

这里唯一值得注意的是,Flutter 没有官方的 Docker 镜像。 您可以在 Automation 构建中使用非官方镜像,也可以创建自己的自定义镜像。 例如,作业可能如下所示:

job("Build, run tests, and publish") {
    container(displayName = "Run script", image = "cirrusci/flutter") {
        shellScript {
            content = """
                flutter test
                flutter pub token add https://packages.jetbrains.team/dart/p/sasmp/my-dart-repo --env-var JB_SPACE_CLIENT_TOKEN
                flutter pub publish -f
            """
        }
    }
}

Space Packages 仓库中已发布的 Flutter 软件包将如下所示:

Space Packages 仓库中的已发布 Flutter 软件包

镜像 pub.dev

如果您使用 Space Automation 构建 Dart 项目,那么可以创建 pub.dev 的本地镜像。 它是 Space Packages 中的一种仓库类型,可以用作拉通缓存。  当某个软件包收到请求时,Space 首先在本地查找软件包,如果不可用,则从远程仓库下载。 了解详情

我们希望这能让您清楚地了解 Space Packages 如何帮助您更安全、更高效地管理和分发 Dart 软件包。 无论您是小型团队还是大型企业的一员,Space Packages 都可以简化您的软件包管理工作流并实现更快的协作。

免费开始

我们期待您对 Space Dart 仓库和 Space Packages 的反馈,欢迎在下方评论区分享您的想法。

本博文英文原作者:

Sue

Evgenia Verbina

Sue

Alexey Totin

]]>
https://blog.jetbrains.com/space/2023/05/31/space-dart-flutter/
Compose Multiplatform for iOS 现已进入 Alpha 阶段 https://blog.jetbrains.com/zh-hans/kotlin/2023/05/compose-multiplatform-for-ios-alpha/ Fri, 26 May 2023 01:00:00 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/05/compose-blog-featured-image-1280x600-1-2.png https://blog.jetbrains.com/?post_type=kotlin&p=357781 JetBrains 出品的声明式框架 Compose Multiplatform 使用 Kotlin 构建跨平台用户界面,现在支持以 iOS 为目标! 这意味着通过 Compose Multiplatform 可以一次性 100% 在 Kotlin 中构建用户界面,并在 Android、iOS 和其他平台上使用。

以下是此版本的关键信息概况:

  • JetBrains 发布了 Alpha 版 Compose Multiplatform for iOS,表示其已可用于实验和小型项目。 尝试一下,通过 Kotlin 助力塑造共享移动用户界面的未来!
  • Compose Multiplatform 使用的 API 已经用于 Android 上的 UI 开发。 拥有现代 Android 开发背景的开发者可以非常轻松地掌握 Compose Multiplatform for iOS。 新手也能够在既有概念和最佳做法的基础上开始学习。
  • 作为 Alpha 版本,Compose Multiplatform for iOS 仍有许多方面尚未完善。 我们需要社区的帮助来塑造共享 Kotlin 用户界面的未来,因此,请试用这项技术并分享您的反馈

开始使用 Compose for iOS

JetBrains 在 KotlinConf 2023 上首次公布了 Compose Multiplatform for iOS。 要详细了解 Compose Multiplatform for iOS 的实际运作,请观看 Sebastian Aigner 和 Nikita Lipsky 主讲的“Compose Multiplatform on iOS”公告演讲录像:

 

推动 Kotlin Multiplatform 超越业务逻辑

Kotlin Multiplatform 此前已经为 Kotlin 开发者提供了可靠的代码共享方式。 业务逻辑能够在不同平台之间共享,对针对特定平台的 API 和功能的访问也得以保留 – 这种方式已经在生产中获得许多应用程序的验证,有越来越多的公司开始利用它来消除应用程序中不必要的逻辑重复。

但 Kotlin Multiplatform 的故事缺少了一部分:那就是不希望为每个目标平台构建和维护单独的用户界面时可以使用的解决方案。 造成这种情况的原因有很多:比如,缺乏资源或人手为每个目标平台提供定制实现。 或者,您可能需要通过快速迭代尽快将应用程序交到用户手中,无法花时间手动保持两个或更多 UI 实现同步。

Compose Multiplatform 解决了这个问题。 用户界面只需实现一次就可以在目标平台之间共享 – 无论是在 Android 与 iOS 之间共享的典型移动用例,还是包括桌面或 Web 等其他平台。

Compose Multiplatform 是 Kotlin Multiplatform 应用程序的可选层,能够用于一次性构建声明式用户界面并将其用于多个目标平台。

基于 Jetpack Compose 构建

Compose Multiplatform 基于 Google 的 Jetpack Compose 构建,这是现代 Android 开发的推荐 UI 框架,100% Kotlin。 开发 Compose Multiplatform 的 JetBrains 团队与 Google 合作,定期将更改上传到 Jetpack Compose 仓库。

Compose Multiplatform 中的 API 与 Jetpack Compose 中经过测试的 API 相同。 这意味着使用 Jetpack Compose 编写过现代 Android 用户界面的开发者可以直接转移这些技能,面向 iOS 及其他平台使用 Compose Multiplatform 编写共享用户界面。

为表明 API 确实相同,请看以下 Compose Multiplatform 代码的示例片段,它会在用户按下按钮时为图像的可见性设置动画:

@Composable
fun App() {
    MaterialTheme {
        var showImage by remember { mutableStateOf(false) }
        Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
            Button(onClick = {
                showImage = !showImage
            }) {
                Text("Toggle image")
            }
            AnimatedVisibility(showImage) {
                Image(
                    painterResource("compose-multiplatform.xml"),
                    "Compose Multiplatform Logo"
                )
            }
        }
    }
}

如果您以前使用过 Jetpack Compose,那么您应该熟悉其中的大部分代码:Compose Multiplatform 允许您使用与现代 Android 开发完全相同的 API,包括状态管理、布局,甚至动画。 对于 Jetpack Compose 中针对特定系统的任务,例如加载资源,Compose Multiplatform 提供了适用于所有平台的便捷替代方案 – 例如上述代码段中的 painterResource 函数。

如您所见,这段代码可以在 Android 和 iOS 上运行,结果一致:

在 iOS 上,Compose Multiplatform 用户界面通过基于图形库 Skiko 的画布实现渲染。 在 Android 上,Compose Multiplatform 是 Jetpack Compose。 这意味着,如果决定将应用程序迁移到针对特定平台的 UI,您可以继续在 Android 上使用 Compose Multiplatform 应用程序,不会遭受损失,也不必丢弃您已经编写的代码。

基于 Kotlin Multiplatform 构建

使用 Compose Multiplatform 构建的应用是 Kotlin Multiplatform 应用程序,可以使用相同的既定机制访问平台 API,例如传感器、偏好设置、数据存储、加密等。 它们还可以利用 Kotlin Multiplatform 库不断发展的生态系统,这些库为 SDK 提供了从数据库包装器到跨平台包装器的多样内容。 当然,您也可以继续使用独立于 Compose Multiplatform 的 Kotlin Multiplatform 共享业务逻辑、网络和其他抽象。

与 SwiftUI 和 UIKit 视图的互操作性

现实世界应用程序需要访问针对特定设备的功能,在许多情况下,UI 层也不例外:无论是嵌入浏览器还是播放视频,您都可能会想要访问 iOS 提供的内置功能来丰富用户体验。

对于这些情况,Alpha 版 Compose Multiplatform 带有一个原型,用于 UI 层上的双向互操作性。 使用 UIKitView,您可以在共享用户界面中嵌入针对特定平台的复杂微件,如地图、Web 视图、媒体播放器和摄像头画面。 另一方面,通过 ComposeUIViewController,您可以在 SwiftUI 应用程序中嵌入 Compose Multiplatform 屏幕,在 iOS 应用程序中逐步采用 Compose Multiplatform。

Compose Multiplatform for iOS 允许与原生用户界面的双向互操作:您可以在 Compose UI 中嵌入复杂的 UI 视图,例如 MapKit 的 MKMapView,或者在 SwiftUI 应用程序中嵌入 Compose 屏幕。

追求卓越用户体验

Compose Multiplatform for iOS 目前处于 Alpha 阶段,这也意味着许多方面尚未完善。 只有足够细心谨慎,才能跨多个平台提供出色的用户体验。团队希望确保解决所有必要方面,让 Compose Multiplatform 应用程序在任何位置使用起来都舒适和自然。 这从最基本的交互开始,比如手势和滚动物理,它们定义了应用程序的基本感觉。 它进一步扩展到导航原则和转换,以及复杂的交互,如文本选择、输入管理、上下文菜单和类似的交互。

我们也明白,Compose Multiplatform 应用程序需要提供可靠的无障碍功能集成并尊重用户选择,我们致力于提供与底层 iOS 机制的可靠集成,从系统的文本转语音功能,到用户在设备上设置的缩放和对比度偏好设置。

显然,这是一个多样化而广泛的重点领域。 我们希望对每一个要点都给予足够的关注,确保 Compose Multiplatform for iOS 尽可能提供出色的用户体验。 在此过程中,我们要确保将您的需求放在首位,我们期待您的反馈

我们还意识到,即使在高刷新率显示器上,绝对流畅的性能也是提供出色用户体验的主要因素。 因此,Compose Multiplatform 和 Kotlin/Native 团队正在密切合作,提高 iOS 上共享用户界面的性能。

Compose Multiplatform for iOS 主题设置

因为 Compose Multiplatform for iOS 使用基于画布的渲染,默认情况下 iOS 和 Android 应用程序外观相同。 目前,Compose Multiplatform 在所有平台上原生提供 Material 和 Material 3 微件。 您可能已经在 Android 应用程序中熟悉过这些微件。 使用内置的主题功能,您可以跨平台一致调整这些微件的外观来反映品牌形象,也可以使用针对特定平台的自定义主题。

使用 Compose Multiplatform 构建的移动应用目前默认带有 Material 微件。 您可以自定义它们的外观,构建自己的界面组件,甚至应用针对特定平台的主题。

当然,跨平台 UI 框架的一个关键问题是元素应在多大程度上模仿目标平台的外观。 当前阶段,JetBrains 团队尚未就提供原生还是通用的 UI 元素做出决定。 由于这是 Compose Multiplatform 用户体验的关键部分,我们不想在获得开发社区的反馈之前就做出决定,我们欢迎您分享想法和意见

亲自尝试 Compose Multiplatform for iOS!

希望您愿意试用 Compose Multiplatform for iOS! 如前所述,虽然许多方面尚未完善,但也有许多方面已经运作良好,可供试用!

您可以通过多种方式熟悉 Alpha 版 Compose Multiplatform for iOS,包括示例应用程序和项目模板。

开始使用模板

以 Android 和 iOS 为目标开始用 Compose Multiplatform 编写应用程序的最简单方式是使用官方 GitHub 模板,该模板自带教程,可以帮助您启动您的第一个 Compose Multiplatform 应用。

开始使用 Compose for iOS

使用演示应用程序探索 Compose for iOS

查看示例是探索新技术的好方法。 我们准备了一些示例项目来演示 iOS 及其其他目标上的 Compose Multiplatform。 您可以在 Compose Multiplatform 仓库中找到它们。

探索示例项目

Kotlin Multiplatform Mobile Production Sample 等示例项目现在有一个分支,其中包含基于 Compose Multiplatform 的 UI 实现,可供比较仅在应用之间共享业务逻辑与另外也共享应用程序的 UI 层之间的异同。

分享反馈!

Compose Multiplatform for iOS 处于 Alpha 阶段,我们希望根据您的需求改进技术。

报告问题、告诉我们您认为缺少的 API,以及请求您希望看到的功能,帮助我们为您做出改进。 您可以在项目的问题跟踪器中执行这些操作。

如果您想与 Compose Multiplatform 背后的团队或其他开发者交谈,我们也邀请您加入 Kotlin Slack 上的讨论。 在 #compose-ios 频道中,您可以找到有关 Compose Multiplatform for iOS 的讨论。 在 #compose 频道中,您可以讨论与 Compose Multiplatform 和 Jetpack Compose 相关的一般主题。

我们期待您接下来使用 Compose Multiplatform 构建的内容!

另请参阅

本博文英文原作者:

Sue

Sebastian Aigner

]]>
https://blog.jetbrains.com/pt-br/kotlin/2023/05/compose-multiplatform-for-ios-alpha/ https://blog.jetbrains.com/ko/kotlin/2023/05/compose-multiplatform-for-ios-alpha/ https://blog.jetbrains.com/ja/kotlin/2023/05/compose-multiplatform-for-ios-alpha/ https://blog.jetbrains.com/fr/kotlin/2023/05/compose-multiplatform-for-ios-alpha/ https://blog.jetbrains.com/es/kotlin/2023/05/compose-multiplatform-for-ios-alpha/ https://blog.jetbrains.com/kotlin/2023/05/compose-multiplatform-for-ios-alpha/ https://blog.jetbrains.com/de/kotlin/2023/05/compose-multiplatform-for-ios-alpha/
如何在 PyCharm 中创建密码短语生成器 https://blog.jetbrains.com/zh-hans/pycharm/2023/05/pycharm/ Mon, 22 May 2023 03:14:30 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/05/Blog_Featured_image_1280x600-1.png https://blog.jetbrains.com/?post_type=pycharm&p=357260 在本教程中,您将在 PyCharm 中创建一个密码短语生成器。 您还将学习如何:

  • PyCharm Community Edition 中创建项目。
  • 安装并导入 Python 软件包。
  • 使用 Typer 库在 Python 中创建命令行接口。
  • 在 PyCharm 中运行和调试代码。
  • 创建和编辑运行配置。

本教程的目的是,展示如何使用免费的 PyCharm Community Edition 开发简单的 CLI 应用程序来自动执行日常任务。 虽然在本教程结束时您将获得一个可用的密码短语生成器,但请仅将其视为一个学习项目。 切勿使用此生成器生成的密码短语保护任何真实数据。

克隆仓库即可获取完整的代码。 要了解有关克隆的信息,请参阅 PyCharm 文档

关于密码短语

什么是密码短语?

我们每天都会使用很多密码。 注册使用服务或网站时,您都需要创建一个长而独一无二的密码,包含数字、特殊字符、大写字母等。

这些要求都是为了使密码能够抵抗暴力攻击。 暴力攻击基本上是多次尝试猜测密码,直到最终猜对为止。 需要多少尝试和多长时间取决于密码的长度和复杂度。

密码短语是由多个随机单词组成的密码。 它不需要有意义,也不需要符合语法规则。 密码短语通常包含 4 到 5 个单词,越多越好。 例如,PhysicianBuiltHotPotatoRegularly 就是一个密码短语。

为什么密码短语更好?

A^1rL#2k2oPiA9H 是一个安全系数高的好密码。 它包含大小写字母、数字、特殊符号,长度为 15 个字符。 但是您更愿意记住哪个,A^1rL#2k2oPiA9H 还是 PhysicianBuiltHotPotatoRegularly? 顺便说一下,后者有 32 个字符。

除了记住密码的难易程度之外,我们还应该注意破解密码的难易程度。 来看下表:

  A^1rL#2k2oPiA9H PhysicianBuiltHotPotatoRegularly
符号集大小 95 52
密码长度 15 32
破解所需的尝试次数 (?) 298 2182

两者都很强,但密码短语安全系数更高且更好记。 另外,如果在密码短语中添加一些数字和特殊字符,这将使所需的平均猜测次数增加到 2210 次 – 几乎不可能破解!

综上:

  • 由随机单词组成的密码短语比由随机字符组成的密码更好记。
  • 密码短语通常比密码更长,因此更能抵抗暴力攻击,更安全。
  • 密码短语可以根据复杂度要求进行修改。 例如,您可以将单词大写来包含大写字母,或者在单词之间添加特殊字符和数字作为分隔符。

什么是密码短语生成器

通常,密码短语生成器是一种将随机单词组合成伪句来生成密码的程序。 在本教程中,我们将使用 PyCharmTyper 开发一个命令行工具,它将执行以下操作:

  • 生成由 4-5 个随机单词组成的密码短语。
  • 按照选项将密码短语中的单词大写。 单词默认不大写。
  • 使用任意符号作为单词的分隔符。 默认不含分隔符。
  • 添加第五个单词,创建更长的密码短语。 默认长度为四个单词。

工具不会存储您的密码短语。

前提

第一步

使用 Typer 编写“Hello World”

首次启动 PyCharm 时,您会看到欢迎屏幕。 点击 New Project(新建项目):

PyCharm 的欢迎屏幕

如果 PyCharm 已经在运行,可以从主菜单中选择 File | New Project(文件 | 新建项目)。

New Project(新建项目)窗口打开后,找到顶部的 Location(位置)字段,使用它指定项目的目录。 这也将用作项目名称。

指定项目位置

您可以选择 PyCharm 将在其中安装项目依赖项的虚拟环境类型。 您还可以选择创建环境的位置,以及基础 Python 解释器。

选择首选环境类型并指定选项(或保留默认值),然后点击 Create(创建)。

PyCharm 将创建包含虚拟环境的项目目录(在我们的示例中为 venv)。 如果您在上一步中没有清除 Create a main.py welcome script(创建 main.py 欢迎脚本)复选框,它也会创建 main.py 并在编辑器中将其打开:

PyCharm 中新创建的项目

文件包含带有一些基本指令的“Hello World”脚本。 将以下代码复制到剪贴板:

def main():
    print("Hello World")


if __name__ == "__main__":
    typer.run(main)

转到 PyCharm,按 ⌘A / Ctrl+A,然后按 ⌘V / Ctrl+V 替换 main.py 的内容。 您将得到:

Typer 的 'Hello World'

您可以看到 typer 下方有一条红色波浪线。 这表示 Python 解释器无法识别 Type。 我们需要安装此软件包并将其导入 main.py 才能启动脚本。

将鼠标指针悬停在高亮显示的符号上,然后在弹出窗口中选择 Install and import package ‘typer’(安装并导入软件包 ‘typer’):

安装并导入 typer

PyCharm 会将 Typer 软件包安装到项目环境中,并将其导入 main.py

现在,我们可以运行脚本了。 点击装订区域中的运行图标,选择 Run ‘main’(运行 ‘main’):

使用装订区域图标运行脚本

带有“Hello World”的 Run(运行)工具窗口将在底部打开:

Run(运行)工具窗口

生成第一个密码短语

修改代码,让它打印密码短语而不是“Hello World”。 这里的想法是随机挑选单词并组成短语。 因此,我们需要一个或多个可供选择的单词列表。 您可以手动准备这样的列表,也可以使用大型语言模型生成。

创建单词列表时,请确保它们的安全性。 如果恶意行为者访问了单词列表,他们将能够在几秒钟内破解您的密码。

我们的仓库包含单词列表以及本教程的完整代码。 您可以仅出于学习目的下载和使用,风险自负。 强烈建议不要根据这些单词列表生成真正的密码短语。

在这一步中,您需要包含 4 个单词的列表:

  • obj_nouns.txt,包含将在我们生成的伪句中充当宾语的名词。
  • sub_nouns.txt,包含将充当主语的名词。
  • verbs.txt,包含动词。
  • adjectives.txt,包含形容词。

每个列表中的单词越多,脚本能够生成的组合就越多。 每个单词都应另起一行。

将生成或下载的单词列表复制到项目目录。 如果您想手动创建单词列表,可以在 PyCharm 中进行:

  1. Project(项目)工具窗口中点击项目目录,然后按 ⌘N / Ctrl+N
  2. 选择 File(文件),指定文件名,例如 obj_nouns.txt
  3. PyCharm 将创建文件并在编辑器中将其打开。

项目结构应该如下所示:

项目结构

首先,我们需要从文本文件读取单词。 将 print("Hello World") 替换为以下代码:

sub_nouns = read_words('sub_nouns.txt')

同样,read_words 下方有一条红色波浪线。 我们需要创建这个函数。 将鼠标悬停在 read_words 上,然后在弹出窗口中点击 Create function ‘read_words’(创建函数 ‘read_words’):

创建 read_words 函数

PyCharm 将创建一个函数存根。 指定 file_name 为函数形参,然后按 Tab 开始编写函数代码:

指定函数形参

您可以将高亮显示的代码复制到函数体中:

def read_words(file_name):
    with open(file_name, 'r') as f:
        words = f.readlines()
    return words

该函数将打开名称在第一个形参中提供的文件。 然后,它会应用 readlines() 方法,这将返回包含文件行作为其元素的 Python 列表。 列表被保存到 words 变量中并由函数返回。

我们回到 main() 函数,使用新创建的 read_words 函数读取另外 3 个单词列表:

def main():
    sub_nouns = read_words('sub_nouns.txt')
    verbs = read_words('verbs.txt')
    adjectives = read_words('adjectives.txt')
    obj_nouns = read_words('obj_nouns.txt')

接下来,创建一个单词列表的列表,将其命名为 word_bank。 稍后,我们将在为密码短语选择随机单词时遍历它:

    word_bank = [sub_nouns, verbs, adjectives, obj_nouns]

选择的随机单词将被保存到另一个列表中。 把它命名为 phrase_words 并进行初始化:

    phrase_words = []

在接下来的 for 循环中,我们遍历 word_bank 的条目。 word_bank 中的每个条目都是一个包含单词的列表。 我们从内置 random 模块中调用 SystemRandom() 类的 choice() 方法,从列表中选择一个随机单词。 然后,将所选单词追加到 phrase_words

    for word_list in word_bank:
        random_word = random.SystemRandom().choice(word_list)
        phrase_words.append(random_word)

虽然 random 是内置模块,但我们还是需要导入它。 像之前一样,您可以通过编辑器中的红色波浪线来判断。 将鼠标悬停在它上面,选择 Import this name(导入此名称)。

最后,使用 join 将包含随机选择单词的列表转换成短语并打印结果:

    passphrase = ''.join(phrase_words)
    print(passphrase)

main() 在这个阶段应该是这样的:

def main():
    sub_nouns = read_words('sub_nouns.txt')
    verbs = read_words('verbs.txt')
    adjectives = read_words('adjectives.txt')
    obj_nouns = read_words('obj_nouns.txt')
    word_bank = [sub_nouns, verbs, adjectives, obj_nouns]
    phrase_words = []
    for word_list in word_bank:
        random_word = random.SystemRandom().choice(word_list)
        phrase_words.append(random_word)
    passphrase = ''.join(phrase_words)
    print(passphrase)

现在,运行脚本来检查它是否正常运作。 点击装订区域中的 Run(运行)图标,选择 Run ‘main’(运行 ‘main’),您应该得到:

首次运行脚本

现在已经有了 4 个单词,但它绝对不是短语。 当代码正常工作但产生意外结果时,就需要进行调试。

从当前输出可以看到,脚本成功从单词列表选择了随机单词。 它没有做到的是将单词组合成短语。 main() 的倒数第二行似乎是问题根源:

def main():
    ...
    passphrase = ''.join(phrase_words)
    print(passphrase)

为了查看特定代码行生成的结果,我们应该在该行上放置一个断点。 然后,调试器将在执行带有断点的行之前停止。 要设置断点,请点击我们要检查的行旁边的装订区域:

设置断点

要开始调试,像之前一样点击装订区域中的 Run(运行)图标,但这次,在弹出窗口中选择 Debug ‘main’(调试 ‘main’)。 调试器将在断点处启动并停止执行,在底部打开 Debug(调试)工具窗口:

Debug(调试)工具窗口

Debug(调试)工具窗口的右侧窗格中,您可以看到目前为止已赋值的变量。 展开 phrase_words 来查看其中的内容:

检查 phrase_words 变量

列表中有 4 个类型为 str 的条目。 每个字符串都以新行 (‘n’) 结尾。 因此,后续将这些字符串连接在一起打印时,每个单词都打印在单独的行中。

如果查看其他列表,例如 adjectives,可以发现其中的所有条目也都以 ‘n’ 结尾。 我们从 read_words 函数获取这些列表。 这意味着我们需要修正它,让它返回没有尾随 ‘n’ 的单词列表。

让我们使用 strip() 和列表推导式,在返回之前去除每个列表条目中的 ‘n’:

def read_words(file_name):
    with open(file_name, 'r') as f:
        words = f.readlines()
        words = [word.strip() for word in words]
    return words

重新运行 main(),获得结果:

修正后运行脚本

创建更好的密码短语

让它们更好记

显然,上一步生成的密码短语有些难读。 将每个单词大写来提高可读性呢?

在这个应用程序中使用 Typer,因为我们不想只运行脚本和获取密码短语。 我们需要创建一个命令行接口,用来将各种选项传递给脚本,控制生成的密码短语的属性。 一种选项是将单词大写。

运行 main.py 时,将执行以下行:

...
if __name__ == "__main__":
    typer.run(main)

所以,我们将使用 typer 执行 main 函数。 这是因为 Typer 可以接受命令行实参,然后将它们作为形参传递给函数。

我们在 main() 函数中引入 capitalize 形参,并默认将其设为 False

def main(capitalize = False):
    sub_nouns = read_words('sub_nouns.txt')
    ...
    passphrase = ''.join(phrase_words)
    print(passphrase)

根据 Python 编码最佳做法,我们应该指定形参类型。 将文本光标置于 capitalize 上,按 ⌥Enter/ Alt+Enter。 然后,选择 Specify type for the reference using annotation(使用注解指定引用类型)。 输入 bool,因为 capitalize 应该有一个布尔值。

现在,如果 capitalizeTrue,我们需要在连接单词之前将它们大写。 使用 if capitalize: 开始一条 if 语句。 让我们使用类似于修正 read_words 函数时使用的方法,不过,这次我们将使用实时模板而不是手动编写列表推导式。 输入 compl 并按 Enter。 然后,指定列表推导式的所有元素,按 Tab 移动到下一个元素。

应该得到:

def main(capitalize: bool = False):
    sub_nouns = read_words('sub_nouns.txt')
    verbs = read_words('verbs.txt')
    adjectives = read_words('adjectives.txt')
    obj_nouns = read_words('obj_nouns.txt')
    word_bank = [sub_nouns, verbs, adjectives, obj_nouns]
    phrase_words = []
    for word_list in word_bank:
        random_word = random.SystemRandom().choice(word_list)
        phrase_words.append(random_word)
    if capitalize:
        phrase_words = [phrase_word.capitalize() for phrase_word in phrase_words]
    passphrase = ''.join(phrase_words)
    print(passphrase)

为确保大写正确工作,我们需要在运行 main.py 时将 capitalize 作为实参传递。 为此,我们需要编辑运行配置。 在 IDE 窗口顶部找到 Run(运行)微件:

PyCharm 的 Run(运行)微件

使用这个微件可以选择所需的运行配置,以及在运行或调试模式下启动它。 在我们点击装订区域图标并启动脚本时,PyCharm 已经创建了 main 配置。 点击配置名称,打开菜单,然后选择 Edit configurations(编辑配置):

编辑运行/调试配置

在打开的对话框中,在 Parameters(形参)字段中指定 --capitalize

向配置添加形参

点击 OK(确定)保存更新后的配置。 然后,点击微件中的运行图标。

结果如下:

使用“capitalize”选项运行脚本

为了提升可读性,我们可以将密码短语中的单词分开。 使用特殊字符作为分隔符还将起到另一重作用,生成符合特定密码复杂度要求的密码短语。

编辑 main() 函数的倒数第二行,如下所示:

def main(capitalize: bool = False):
    ...
    passphrase = separator.join(phrase_words)

PyCharm 使用红色波浪线高亮显示 separator,因为函数还没有这个形参。 将鼠标悬停在它上面,在弹出窗口中选择 Create parameter ‘separator’(创建形参 ‘separator’)。 然后,将 '' 指定为其默认值,因为默认情况下我们不想添加分隔符。

也将 str 指定为形参类型。 应该得到:

def main(capitalize: bool = False, separator: str = ''):
    ...
    passphrase = separator.join(phrase_words)
    print(passphrase)

现在,您肯定想查看结果了。 不要忘记先更新运行配置。 点击 Run(运行)微件,选择 Edit configurations(编辑配置),然后在 Parameters(形参)字段中添加新形参:

向运行配置添加另一个形参

运行配置以查看结果:

使用“separator”选项运行脚本

现在,您可以用特殊字符和数字分隔密码短语中的单词。 还可以使用多个符号来满足网站的密码要求,例如“#4”或“%7”。

让它们更难破解

密码短语越长,暴力攻击需要尝试的次数就越多。 让我们向密码短语再添加一个单词。

首先,准备包含副词的第五个单词列表,并将其放入项目目录。 将布尔类型的 long 形参添加到 main() 函数的签名中。 根据这个形参(非必选,默认设置为 False),我们将再向 word_bank 添加一个单词列表:

def main(capitalize: bool = False, separator: str = '', long: bool = False):
    ...
    word_bank = [sub_nouns, verbs, adjectives, obj_nouns]
    if long:
        adverbs = read_words('adverbs.txt')
        word_bank.append(adverbs)
	...

这一次,使用内置终端运行脚本。 按 ⌥F12/ Alt+F12,在打开的 Terminal(终端)工具窗口中输入以下命令:

python main.py --capitalize --separator "1_" --long

结果应该类似于:

使用“long”形参运行脚本

准备工具以供使用

定义短选项名称

如果您使用过 CLI 工具,您应该知道它们通常只允许用户以一个字母指定实参。 我们也将这个功能添加到工具中。 为了提升代码可读性,让我们重新格式化函数签名,让每个形参都在单独的行中:

def main(
    capitalize: bool = False,
    separator: str = '',
    long: bool = False
):
...

然后,将各个形参的默认值替换为 typer.Option(, , )

这是 main() 的最终签名:

def main(
    capitalize: bool = typer.Option(False, '--caps', '-c'),
    separator: str = typer.Option('', '--separator', '-s'),
    long: bool = typer.Option(False, '--long', '-l')
):
...

接下来,我们可以一起指定所有选项。 分隔符 (‘-s’) 应该放在最后,因为它后面需要一个字符串:

使用所有选项运行脚本

记录选项

默认情况下,Typer 还会添加 --help 选项。 来看它现在是如何工作的:

使用“--help”选项运行脚本

我们可以了解存在哪些形参以及它们的长短名称。 再添加一些注释来解释它们的实际作用怎么样? 为 main() 的每个形参添加 help,如下所示:

def main(
    capitalize: bool = typer.Option(False, '--caps', '-c', help='Capitalize each word.'),
    separator: str = typer.Option('', '--separator', '-s', help='Separate words with the given symbol.'),
    long: bool = typer.Option(False, '--long', '-l', help='Make the passphrase longer by including an adverb.')
):
...

--help 现在生成了更多实用信息:

在脚本帮助中显示实用信息

您可能会想在不需要 PyCharm 的情况下使用密码短语生成器,例如在系统终端中。 为此,应该使用以下命令将 Typer 安装到系统解释器:

python3 -m pip install --user typer

总结

在本教程中,您学习了如何:

  • 在 PyCharm Community Edition 中创建项目。
  • 使用 Python 和 Typer 开发人性化 CLI 工具。
  • 使用快速修复和实时模板更快编写代码并避免错误。
  • 调试代码。
  • 使用运行配置和终端在 PyCharm 中运行代码。

 

本博文英文原作者:

Sue

Denis Mashutin

]]>
https://blog.jetbrains.com/ja/pycharm/2023/05/pycharm/ https://blog.jetbrains.com/pycharm/2023/05/pycharm/
IntelliJ IDEA 中的“快速”功能 https://blog.jetbrains.com/zh-hans/idea/2023/05/intellij-idea-4/ Tue, 16 May 2023 03:00:00 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/04/blog-cover.png https://blog.jetbrains.com/?post_type=idea&p=352835 一些 IntelliJ IDEA 功能或操作包括术语“快速”。 它们会加快某些流程吗? 我们来一探究竟。 在这篇博文中,我将介绍这些功能是什么、为什么需要它们以及如何使用它们。

 

1. 快速列表

如果您有经常使用的操作,但由于大多数按键组合通常被其他快捷键占用而没有指定快捷键,那么快速列表可以提供帮助。

使用快速列表,您可以创建一个收藏操作的简短列表,并为其指定快捷键。 列表中的每个操作将由从 0 到 9 的数字标识。 要访问此列表,您只需记住列表上每个操作对应的快捷键和编号。

要创建新列表,请访问设置并在 Appearance and Behavior(外观与行为)下找到 Quick List(快速列表)。

创建一个新列表并为其指定名称和简短描述。 随后转到按键映射,为刚刚创建的列表指定快捷键。 即使您在实际使用时忘记此列表的名称,也无需担心。 您可以使用快捷键访问列表,或者使用 Search Everywhere(随处搜索)来查找和使用。 下面的 gif 显示了这些步骤:

此外,IntelliJ IDEA 还提供了预定义列表,例如 Refactor this(重构此)、VCS operations(VCS 操作)等,但您无法修改这些列表。

2. Quick Switch Scheme(快速切换方案)(Ctrl + `)

Quick Switch Scheme(快速切换方案)是一个便利的预定义功能列表,其中包含有关如何在 IDE 中的各种显示方案(例如,整体主题、编辑器配色方案、按键映射、视图模式和缩放等)之间快速切换的功能。

您可以使用快捷键(Windows/Linux 上的 Ctrl + ~ 和 macOS 上的 Cmd + ~)或使用查找操作 Quick Switch Scheme(快速切换方案)来调用此功能。

选择 Theme(主题)选项时,您可以循环浏览适用于您的 IDE 的主题。 如果您在所选主题上等待 3 秒,则可以在选择之前查看该主题的外观。 您还可以从此处安装新主题。

利用按键映射选项,您可以将当前按键映射替换为其他可用选项的列表。 您也可以选择配置或安装按键映射。

通过使用视图模式,您可以在五种预定义的视图模式之间切换 – 演示模式、免打扰模式、全屏模式、Zen 模式和紧凑模式。 默认情况下,IDE 不会为它们指定快捷键,但是,如果您指定自定义快捷键,它们将在此处显示。

缩放选项可以用于增大 IDE 的整体字体大小。 如果您正在向同事或其他人展示代码并希望在 IDE 中更改缩放比例,这项功能非常方便。

3. Quick Definition(快速定义)

假设您正在项目中滚动浏览 ShoppingCart 类,但不确定另一个类型 ShoppingItem 的内容。 有没有办法在不丢失编码流程的前提下阅读其内容? 您可以使用 Go to Declaration(转到声明)功能导航到 ShoppingItem 类的源代码,但这会在另一个窗口中打开该源代码,从而中断您的编码流程。

相反,您可以使用 Quick Definition(快速定义)功能并在编辑器窗口中以弹出窗口的形式打开任何其他类型(类、接口、记录、枚举等)的定义。 只需将光标置于类型上并调用 Quick Definition(快速定义)(快捷键 – Win/Linux 上为 Ctrl+Shift+I,macOS 上为 Cmd+Shift+I,或者点击主菜单中的 View | Quick Definition(视图 | 快速定义))。

下面的 gif 显示了此过程:

您也可能不记得变量是在何处或如何定义的,这在阅读长方法时经常发生。 您可以使用此功能来查看符号(例如类、字段、方法、函数或标记)是在何处以及如何定义的。 使用更受欢迎的 Go to Declaration(转到声明)功能可能会中断您的流程。 使用 Quick Definition(快速定义)是一个很好的选择,如下面的 gif 中所示:

当您使用 Quick Definition(快速定义)时,它不会显示类型的文档,即以 /** 开头并以 */ 结尾的 Javadoc 注释。 但是,它确实会显示其他注释,如多行注释(以 /* 开头并以 */ 结尾)和以 // 开头的单行注释

有没有办法以类似方式查看类型的文档? 有! 答案是 Quick Documentation(快速文档)。

4. Quick Documentation(快速文档) (Ctrl + Q)

在编辑器窗口中,您可以使用 Quick Documentation(快速文档)查看弹出窗口中显示的类型的 JavaDoc 文档。

假设您正在浏览类 ShoppingCart 的来源,这个类定义了一个 List 类型的实例变量,并且您的类从 List 调用 add 方法。 您想了解文档对 add 方法的描述,而不想深入研究此方法的代码。

为此,您可以将光标移动到 add 方法调用,然后使用快捷键 Ctrl+Q(适用于 Windows/Linux)和 Cmd+Q(适用于 macOS)。 下面的 gif 显示了此过程:

总结

“快速”功能可能非常有用。

快速列表可以帮助您对与您的任务(例如,部署或代码演示)相关的操作进行分组。 Quick Switch Scheme(快速切换方案)是一个包括在 IDE、主题、视图、按键映射、缩放等各种 UI 元素之间进行切换的操作的列表。

利用 Quick definition(快速定义)和 Quick Documentation(快速文档),您可以在 IDE 中以弹出窗口的形式引用类、方法、变量等的定义或文档。 当您在单独的窗口中导航到某个类的源代码时,这有助于防止您的编码流程被中断。

快乐编程!

 

本博文英文原作者:

Sue

Mala Gupta

]]>
https://blog.jetbrains.com/ko/idea/2023/05/intellij-idea-4/ https://blog.jetbrains.com/idea/2023/05/intellij-idea-4/
Qodana 2023.1:灵活的配置文件配置,支持迁移到 Kotlin/JS IR 编译器,检查 Go 的许可兼容性,插件集成,以及 30 多项新检查 https://blog.jetbrains.com/zh-hans/qodana/2023/05/qodana-2023-1-kotlin-js-ir-go-30/ Mon, 15 May 2023 03:00:00 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/04/Blog_Featured_image_1280x600-3-1.png https://blog.jetbrains.com/?post_type=qodana&p=352833 Qodana 是 JetBrains 开发的智能代码质量平台,目前处于预览阶段。 这款强大的静态分析引擎可以将检查从 JetBrains IDE 带到任何 CI 管道,在 CI 服务器上运行资源密集型检查,为您节省时间和计算资源。 支持 60 多种技术,分析无限行数的代码。

新版 Qodana 拥有重要的增强功能,可以帮助您确保代码具有最高质量。 2023.1 版让 CI 管道的静态分析步骤拥有更大的灵活性。 我们引入了一种针对项目扫描配置检查的智能方式,并实现了可能是向 Qodana 静态分析引擎添加插件的最简单方式。

请继续查看重大更新的完整列表,或直接跳到 linter 特定的增强功能。

免费试用 Qodana

重大更新

使用新的 YAML 格式实现灵活的配置文件配置

检查配置文件是 Qodana 配置的关键部分。 它们定义了在运行分析时 Qodana 将对代码应用的一组检查。

从 2023.1 版开始,Qodana 以人类可读的 YAML 格式提供了灵活的配置文件配置功能,这使您可以更轻松地自定义检查配置文件,并随着项目的进展进行调整。 下面是用于优化分析的选项:

  • [新] 将检查分组并管理此类群组。 
  • [新] 包括现有配置文件和创建新配置文件。
  • 启用和禁用检查。
  • 设置检查参数。
  • 配置检查范围。

例如,在使用 PHP/常规检查类别检查代码时,可以从 Qodana For PHP linter 中排除 PhpDeprecationInspection 检查。

name: "PHP/General without PhpDeprecationInspection"
inspections:
  - group: "category:PHP/General"
    enabled: true # Enable the 'PHP/General' category
  - inspection: PhpDeprecationInspection
    enabled: false # Disable the PhpDeprecationInspection inspection

请参阅此文档,详细了解自定义配置文件。 

默认配置文件的重大重构

Qodana 提供两种类型的默认配置文件 – qodana.starter,它仅用于重要检查,是项目初始扫描的理想选择,以及 qodana.recommended,它会启用一组广泛适用于大多数项目的预先选择的检查。

我们重写了这两个配置文件,使它们与 IDE 中提供的默认配置文件保持一致。 这一变化可以让您获得更高的透明度,使您能够确保 Qodana 和您的 IDE 运行的检查基本相同,从而有助于对代码质量采用更一致的方式。 

您可以在 GitHub 中找到更新的 qodana.recommended 配置文件。 此配置文件与默认的 IDE 配置文件相匹配,但有几项例外:

  • 对于更轻量级的分析,所有全局检查都会被禁用,因为它们可能非常耗时。  
  • 默认情况下会排除某些项目目录(如构建目录和测试源),不对其进行分析。

您也可以在 GitHub 中找到更新的 qodana.starter 配置文件。 此配置文件派生自 qodana.recommended 配置文件,仅执行重要检查。 以下是我们做出的主要更改:

  • 禁用了代码样式代码约定语义检查。 
  • 禁用了对代码库中不可执行部分(例如 Javadocs)的检查。

轻松地向 Qodana 添加插件

如果想要扩展 Qodana 并使用 JetBrains Marketplace 中的检查插件,只需将其 ID 添加到 qodana.yaml 文件中即可。 

得益于 Marketplace 的最近更新,您现在可以直接在插件页面中复制其 ID。

可以通过两种方式安装插件所需的第三方软件:

  • 使用 bootstrap 选项。
  • 开发一个以 FROM jetbrains/qodana... 开头的自定义 Dockerfile。您可以使用 GitHub 上提供的 Qodana Dockerfile 示例。

下面是一些您可能想要了解的值得注意并且非常受欢迎的插件:

  • Grazie Professional,它提供了先进的自然语言书写辅助功能,包括拼写检查、语法校正、自然语言补全以及其他对文本书写和编辑有用的工具。 
  • CheckStyle,它可以在 IntelliJ IDEA 中使用 CheckStyle 对 Java 文件进行实时和按需扫描。
  • Zero Width Characters Locator,它增加了一项检查,可以防止出现与源代码和资源中不可见的零宽度字符相关的一些难以发现的错误。 

您可以在 JetBrains Marketplace 中通过此搜索找到更多插件。 

Qodana 目前不支持付费插件,因此可能仍然存在与当前版本不兼容的插件。 今年,我们打算为 JetBrains Marketplace 上的 Qodana 插件引入适当的兼容性检查。

Linter 特定的更新

Qodana 中的新 JVM 检查

适用于 Kotlin/JS IR 编译器的检查

Kotlin 团队引入了新的 IR 编译器,它可以将 Kotlin 源代码迁移到 JavaScript。 为了简化迁移过程并发现潜在的错误,Qodana 现在支持以下检查:

NonExternalClassifierExtendingStateOrProps

使用从纯 JS 类派生的 Kotlin 接口和类(包括数据类),例如 React 的 StateProps,可能导致 ClassCastExceptions。 出现此类异常的原因是编译器试图将这些类的实例当作 Kotlin 对象来处理,但它们实际上来自 JS。

解决办法是将所有派生自纯 JS 类的类和接口转换为外部接口。 Qodana 将报告扩展 StateProps 使用的非外部分类器。 

NonVarPropertyInExternalInterface

Kotlin/JS 代码中外部接口的属性不能为只读 (val) 属性,因为只有在使用 js()jso() 创建对象后才能为它们赋值。 解决办法是将外部接口的所有属性转换为 var

Qodana 会高亮显示不是 var 的所有属性,以便您可以转换它们。

SpringTransactionalMethodCallsInspection

Spring 框架让您可以使用代码注解管理应用程序事务。 只有标记为 `@Transaction` 的方法的外部调用才会在该事务下运行,这意味着自调用不会启动新的事务。 通过这项新检查,Qodana 可以高亮显示此类情况,因为它们会在事务管理中导致错误。

JUnitMixedFramework

在一个项目中使用多个版本的 JUnit 可能会导致错误,因为开发者会错误地在一个测试用例中混合不同的版本。 例如,考虑以下代码:

测试使用 JUnit4 运行,因此 JUnit5 的 BeforeEach 注解将不被处理,测试将失败并出现 NullPointerException。 

默认情况下,Qodana 不检查测试源,因此要包含此检查,需要向 qodana.yaml 添加以下配置:

include:
  - name: JUnitMixedFramework
    paths:
    - <path-to-tests-directory>

MathRoundingWithIntArgument

此检查报告会对 MathStrictMathround()ceil()floor()rint() 方法的调用。 对整数调用这些方法是多余的,这表明出现了错误,例如意外的整数除法。

试用 Qodana for JVM

Go 的第三方许可审核

如果您依赖开源组件、外包开发或重复使用 GitHub 等服务中的代码,则需要确保使用合适的许可。 您可能会意外地将具有受限许可的库导入软件代码库或忘记更新过期的许可。 如果是这样,您所在的机构可能会面临声誉和法律方面的风险。

为了尽量降低此类风险,我们去年为 Java、Kotlin、PHP 和 JavaScript 引入了许可审核功能。 在此版本中,我们增加了对 Go 的支持。 Qodana 将列出所分析仓库中的依赖项许可,并就有关其与项目许可兼容性的任何问题向您发出警告,如下例所示:

下面是依赖项许可列表示例:

试用 Qodana for Go

针对 .NET 的更新

检查配置文件现在会遵循仓库设置

以前,.NET linter 的检查配置文件中的检查严重性设置基于从 ReSharper 获得的默认值。 但是,当某个项目已使用 ReSharper 和 Rider 的解决方案和项目配置层以及存储在仓库中的 .editorconfig 文件时,这种方式会导致重复设置。 这意味着如果您对其中任意层进行更改,就必须在 Qodana YAML 配置文件中进行相同的更改。 

如果仓库中提供,检查配置文件现在会自动继承 ReSharper 和 .editorconfig 文件中的设置。 不再需要动手完成任何工作! 

Qodana YAML 配置设置部分现已更名

qodana.yaml 配置文件以前包含 dot-net 部分,让您可以指定要打开的解决方案或项目,以及配置和平台选项。 此部分现已更名为 dotnet,从 Qodana 2023.2 版开始,将弃用 dot-net 名称。

试用 Qodana for .NET

如何升级到 Qodana 2023.1

如果使用 latest Docker 标记运行 Qodana,则无需进行任何更改。 如果不是,请在要使用 Qodana 的 CI 工作流中更新到 2023.1 版。 可以在我们的文档中详细了解现有的 CI 集成。   

并非所有检查都会自动启用。 要在分析中排除或包括特定检查,可能需要自定义检查配置文件。

 

这就是 Qodana 2023.1 的亮点! 有关详情,请查看版本说明。 我们希望您喜欢这个新版本。 如果您对未来的博客主题有建议,或者想详细了解 Qodana 可以如何帮助您和您的业务,请在此处发表评论,在 Twitter 上提及我们,或发送电子邮件至 qodana-support@jetbrains.com 与我们联系。

祝您开发愉快并轻松保持代码整洁!

您的 Qodana 团队

发布让您感到自豪的代码

 

本博文英文原作者:

Sue

Valerie Kuzmina

]]>
https://blog.jetbrains.com/ko/qodana/2023/05/qodana-2023-1-kotlin-js-ir-go-30/ https://blog.jetbrains.com/ja/qodana/2023/05/qodana-2023-1-kotlin-js-ir-go-30/ https://blog.jetbrains.com/fr/qodana/2023/05/qodana-2023-1-kotlin-js-ir-go-30/ https://blog.jetbrains.com/qodana/2023/05/qodana-2023-1-kotlin-js-ir-go-30/