Skip to content

Commit c5f11e5

Browse files
authored
Merge pull request #772 from bitjammer/acgarland/help-intro
Add help intro
2 parents f1761dc + 2fab30d commit c5f11e5

File tree

9 files changed

+169
-35
lines changed

9 files changed

+169
-35
lines changed

Sources/SwiftDriver/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ add_library(SwiftDriver
7878
Jobs/Planning.swift
7979
Jobs/PrintTargetInfoJob.swift
8080
Jobs/ReplJob.swift
81+
Jobs/SwiftHelpIntroJob.swift
8182
Jobs/Toolchain+InterpreterSupport.swift
8283
Jobs/Toolchain+LinkerSupport.swift
8384
Jobs/VerifyDebugInfoJob.swift

Sources/SwiftDriver/Driver/CompilerMode.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232

3333
/// Dump information about a precompiled Clang module
3434
case dumpPCM
35+
36+
/// Introduce the user to Swift concepts depending on context.
37+
case intro
3538
}
3639

3740
/// Information about batch mode, which is used to determine how to form
@@ -46,7 +49,7 @@ extension CompilerMode {
4649
/// Whether this compilation mode uses -primary-file to specify its inputs.
4750
public var usesPrimaryFileInputs: Bool {
4851
switch self {
49-
case .immediate, .repl, .singleCompile, .compilePCM, .dumpPCM:
52+
case .immediate, .repl, .singleCompile, .compilePCM, .dumpPCM, .intro:
5053
return false
5154

5255
case .standardCompile, .batchCompile:
@@ -57,7 +60,7 @@ extension CompilerMode {
5760
/// Whether this compilation mode compiles the whole target in one job.
5861
public var isSingleCompilation: Bool {
5962
switch self {
60-
case .immediate, .repl, .standardCompile, .batchCompile:
63+
case .immediate, .repl, .standardCompile, .batchCompile, .intro:
6164
return false
6265

6366
case .singleCompile, .compilePCM, .dumpPCM:
@@ -67,7 +70,7 @@ extension CompilerMode {
6770

6871
public var isStandardCompilationForPlanning: Bool {
6972
switch self {
70-
case .immediate, .repl, .compilePCM, .dumpPCM:
73+
case .immediate, .repl, .compilePCM, .dumpPCM, .intro:
7174
return false
7275
case .batchCompile, .standardCompile, .singleCompile:
7376
return true
@@ -93,7 +96,7 @@ extension CompilerMode {
9396
switch self {
9497
case .batchCompile, .singleCompile, .standardCompile, .compilePCM, .dumpPCM:
9598
return true
96-
case .immediate, .repl:
99+
case .immediate, .repl, .intro:
97100
return false
98101
}
99102
}
@@ -116,6 +119,8 @@ extension CompilerMode: CustomStringConvertible {
116119
return "compile Clang module (.pcm)"
117120
case .dumpPCM:
118121
return "dump Clang module (.pcm)"
122+
case .intro:
123+
return "introduction to Swift and packages"
119124
}
120125
}
121126
}

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,7 @@ extension Driver {
809809
// If it is the "built-in" 'repl', then use the normal driver.
810810
if firstArg == "repl" {
811811
updatedArgs.remove(at: 1)
812+
updatedArgs.append("-repl")
812813
return (.normal(isRepl: true), updatedArgs)
813814
}
814815

@@ -1406,7 +1407,15 @@ extension Driver {
14061407
}
14071408

14081409
if driverKind == .interactive {
1409-
return parsedOptions.hasAnyInput ? .immediate : .repl
1410+
if parsedOptions.hasAnyInput {
1411+
return .immediate
1412+
} else {
1413+
if parsedOptions.contains(Option.repl) {
1414+
return .repl
1415+
} else {
1416+
return .intro
1417+
}
1418+
}
14101419
}
14111420

14121421
let useWMO = parsedOptions.hasFlag(positive: .wholeModuleOptimization, negative: .noWholeModuleOptimization, default: false)
@@ -2001,7 +2010,7 @@ extension Driver {
20012010
}
20022011

20032012
// The REPL and immediate mode do not support module output
2004-
if moduleOutputKind != nil && (compilerMode == .repl || compilerMode == .immediate) {
2013+
if moduleOutputKind != nil && (compilerMode == .repl || compilerMode == .immediate || compilerMode == .intro) {
20052014
diagnosticsEngine.emit(.error_mode_cannot_emit_module)
20062015
moduleOutputKind = nil
20072016
}
@@ -2011,7 +2020,9 @@ extension Driver {
20112020
var moduleNameIsFallback = false
20122021
if let arg = parsedOptions.getLastArgument(.moduleName) {
20132022
moduleName = arg.asSingle
2014-
} else if compilerMode == .repl {
2023+
} else if compilerMode == .repl || compilerMode == .intro {
2024+
// TODO: Remove the `.intro` check once the REPL no longer launches
2025+
// by default.
20152026
// REPL mode should always use the REPL module.
20162027
moduleName = "REPL"
20172028
} else if let outputArg = parsedOptions.getLastArgument(.o) {

Sources/SwiftDriver/IncrementalCompilation/IncrementalCompilationState+Extensions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ fileprivate extension CompilerMode {
101101
var supportsIncrementalCompilation: Bool {
102102
switch self {
103103
case .standardCompile, .immediate, .repl, .batchCompile: return true
104-
case .singleCompile, .compilePCM, .dumpPCM: return false
104+
case .singleCompile, .compilePCM, .dumpPCM, .intro: return false
105105
}
106106
}
107107
}

Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ extension Driver {
6464
commandLine.appendFlag(.target)
6565
commandLine.appendFlag(targetTriple.triple)
6666
}
67+
case .intro:
68+
break
6769
}
6870

6971
// Pass down -clang-target.
@@ -270,7 +272,7 @@ extension Driver {
270272
}
271273

272274
// Repl Jobs shouldn't include -module-name.
273-
if compilerMode != .repl {
275+
if compilerMode != .repl && compilerMode != .intro {
274276
commandLine.appendFlags("-module-name", moduleOutputInfo.name)
275277
}
276278

Sources/SwiftDriver/Jobs/Planning.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ extension Driver {
699699
}
700700

701701
// The REPL doesn't require input files, but all other modes do.
702-
guard !inputFiles.isEmpty || compilerMode == .repl else {
702+
guard !inputFiles.isEmpty || compilerMode == .repl || compilerMode == .intro else {
703703
if parsedOptions.hasArgument(.v) {
704704
// `swiftc -v` is allowed and prints version information.
705705
return ([], nil)
@@ -735,6 +735,20 @@ extension Driver {
735735
throw PlanningError.dumpPCMWrongInputFiles
736736
}
737737
return ([try generateDumpPCMJob(input: inputFiles.first!)], nil)
738+
case .intro:
739+
// TODO: Remove this check once the REPL no longer launches
740+
// by default.
741+
if !inputFiles.isEmpty {
742+
throw PlanningError.replReceivedInput
743+
}
744+
// end TODO.
745+
746+
var introJobs = try helpIntroJobs()
747+
748+
// TODO: Remove this to stop launching the REPL by default.
749+
introJobs.append(try replJob())
750+
751+
return (introJobs, nil)
738752
}
739753
}
740754
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===--------------- SwiftHelpIntroJob.swift - Swift REPL -----------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
import SwiftOptions
13+
14+
extension Driver {
15+
mutating func helpIntroJobs() throws -> [Job] {
16+
return [
17+
Job(
18+
moduleName: moduleOutputInfo.name,
19+
kind: .versionRequest,
20+
tool: .absolute(try toolchain.getToolPath(.swiftCompiler)),
21+
commandLine: [.flag("--version")],
22+
inputs: [],
23+
primaryInputs: [],
24+
outputs: [],
25+
requiresInPlaceExecution: false),
26+
Job(
27+
moduleName: moduleOutputInfo.name,
28+
kind: .help,
29+
tool: .absolute(try toolchain.getToolPath(.swiftHelp)),
30+
commandLine: [.flag("intro")],
31+
inputs: [],
32+
primaryInputs: [],
33+
outputs: [],
34+
requiresInPlaceExecution: false
35+
),
36+
]
37+
}
38+
}

Sources/swift-help/main.swift

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ import ArgumentParser
1616
enum HelpTopic: ExpressibleByArgument, CustomStringConvertible {
1717
case driver(DriverKind)
1818
case subcommand(Subcommand)
19+
case intro
1920

2021
init?(argument topicName: String) {
2122
if let kind = DriverKind(rawValue: topicName) {
2223
self = .driver(kind)
2324
} else if let subcommand = Subcommand(rawValue: topicName) {
2425
self = .subcommand(subcommand)
26+
} else if topicName == "intro" {
27+
self = .intro
2528
} else {
2629
return nil
2730
}
@@ -33,6 +36,8 @@ enum HelpTopic: ExpressibleByArgument, CustomStringConvertible {
3336
return kind.rawValue
3437
case .subcommand(let command):
3538
return command.rawValue
39+
case .intro:
40+
return "intro"
3641
}
3742
}
3843
}
@@ -43,13 +48,13 @@ enum Subcommand: String, CaseIterable {
4348
var description: String {
4449
switch self {
4550
case .build:
46-
return "SwiftPM - Build sources into binary products"
51+
return "Build Swift packages"
4752
case .package:
48-
return "SwiftPM - Perform operations on Swift packages"
53+
return "Create and work on packages"
4954
case .run:
50-
return "SwiftPM - Build and run an executable product"
55+
return "Run a program from a package"
5156
case .test:
52-
return "SwiftPM - Build and run tests"
57+
return "Run package tests"
5358
}
5459
}
5560
}
@@ -65,19 +70,60 @@ struct SwiftHelp: ParsableCommand {
6570
help: "List hidden (unsupported) options")
6671
var showHidden: Bool = false
6772

73+
enum Color256: CustomStringConvertible {
74+
case reset
75+
case color(foreground: UInt8?, background: UInt8?)
76+
77+
var description: String {
78+
switch self {
79+
case .reset:
80+
return "\u{001B}[0m"
81+
case let .color(foreground, background):
82+
let foreground = foreground.map { "\u{001B}[38;5;\($0)m" } ?? ""
83+
let background = background.map { "\u{001B}[48;5;\($0)m" } ?? ""
84+
return foreground + background
85+
}
86+
}
87+
}
88+
89+
func printIntro() {
90+
let is256Color = ProcessEnv.vars["TERM"] == "xterm-256color"
91+
let orangeRed = is256Color ? "\u{001b}[1;38;5;196m" : ""
92+
let plain = is256Color ? "\u{001b}[0m" : ""
93+
let plainBold = is256Color ? "\u{001b}[1m" : ""
94+
95+
print("""
96+
97+
\(orangeRed)Welcome to Swift!\(plain)
98+
99+
\(plainBold)Subcommands:\(plain)
100+
101+
""")
102+
103+
let maxSubcommandNameLength = Subcommand.allCases.map { $0.rawValue.count }.max()!
104+
105+
for command in Subcommand.allCases {
106+
let padding = String(repeating: " ", count: maxSubcommandNameLength - command.rawValue.count)
107+
print(" \(plainBold)swift \(command.rawValue)\(plain)\(padding) \(command.description)")
108+
}
109+
110+
// `repl` not included in `Subcommand`, also print it here.
111+
do {
112+
let padding = String(repeating: " ", count: maxSubcommandNameLength - "repl".count)
113+
print(" \(plainBold)swift repl\(plain)\(padding) Experiment with Swift code interactively (default)")
114+
}
115+
116+
print("\n Use \(plainBold)`swift --help`\(plain) for descriptions of available options and flags.")
117+
print("\n Use \(plainBold)`swift help <subcommand>`\(plain) for more information about a subcommand.")
118+
print()
119+
}
120+
68121
func run() throws {
69122
let driverOptionTable = OptionTable()
70123
switch topic {
71124
case .driver(let kind):
72125
driverOptionTable.printHelp(driverKind: kind, includeHidden: showHidden)
73-
print("\nSUBCOMMANDS (swift <subcommand> [arguments]):")
74-
let maxSubcommandNameLength = Subcommand.allCases.map(\.rawValue.count).max()!
75-
for subcommand in Subcommand.allCases {
76-
let padding = String(repeating: " ", count: maxSubcommandNameLength - subcommand.rawValue.count)
77-
print(" \(subcommand.rawValue):\(padding) \(subcommand.description)")
78-
}
79-
print("\n Use \"swift help <subcommand>\" for more information about a subcommand")
80-
126+
printIntro()
81127
case .subcommand(let subcommand):
82128
// Try to find the subcommand adjacent to the help tool.
83129
// If we didn't find the tool there, let the OS search for it.
@@ -98,6 +144,8 @@ struct SwiftHelp: ParsableCommand {
98144
} else {
99145
try exec(path: path.pathString, args: [execName, "help"] + subtopics)
100146
}
147+
case .intro:
148+
printIntro()
101149
}
102150
}
103151
}

0 commit comments

Comments
 (0)