Skip to content

Add help intro #772

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Sources/SwiftDriver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ add_library(SwiftDriver
Jobs/Planning.swift
Jobs/PrintTargetInfoJob.swift
Jobs/ReplJob.swift
Jobs/SwiftHelpIntroJob.swift
Jobs/Toolchain+InterpreterSupport.swift
Jobs/Toolchain+LinkerSupport.swift
Jobs/VerifyDebugInfoJob.swift
Expand Down
13 changes: 9 additions & 4 deletions Sources/SwiftDriver/Driver/CompilerMode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@

/// Dump information about a precompiled Clang module
case dumpPCM

/// Introduce the user to Swift concepts depending on context.
case intro
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that this PR does not actually make use of this. I am guessing the intent is to have this become the interactive/contextual driver invocation kind in the future when swift no longer launches the REPL?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this calls us to rename this enum into DriverMode, if we're introducing modes of operation to it that may have little to do with the compiler.

}

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

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

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

public var isStandardCompilationForPlanning: Bool {
switch self {
case .immediate, .repl, .compilePCM, .dumpPCM:
case .immediate, .repl, .compilePCM, .dumpPCM, .intro:
return false
case .batchCompile, .standardCompile, .singleCompile:
return true
Expand All @@ -93,7 +96,7 @@ extension CompilerMode {
switch self {
case .batchCompile, .singleCompile, .standardCompile, .compilePCM, .dumpPCM:
return true
case .immediate, .repl:
case .immediate, .repl, .intro:
return false
}
}
Expand All @@ -116,6 +119,8 @@ extension CompilerMode: CustomStringConvertible {
return "compile Clang module (.pcm)"
case .dumpPCM:
return "dump Clang module (.pcm)"
case .intro:
return "introduction to Swift and packages"
}
}
}
17 changes: 14 additions & 3 deletions Sources/SwiftDriver/Driver/Driver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,7 @@ extension Driver {
// If it is the "built-in" 'repl', then use the normal driver.
if firstArg == "repl" {
updatedArgs.remove(at: 1)
updatedArgs.append("-repl")
return (.normal(isRepl: true), updatedArgs)
}

Expand Down Expand Up @@ -1406,7 +1407,15 @@ extension Driver {
}

if driverKind == .interactive {
return parsedOptions.hasAnyInput ? .immediate : .repl
if parsedOptions.hasAnyInput {
return .immediate
} else {
if parsedOptions.contains(Option.repl) {
return .repl
} else {
return .intro
}
}
}

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

// The REPL and immediate mode do not support module output
if moduleOutputKind != nil && (compilerMode == .repl || compilerMode == .immediate) {
if moduleOutputKind != nil && (compilerMode == .repl || compilerMode == .immediate || compilerMode == .intro) {
diagnosticsEngine.emit(.error_mode_cannot_emit_module)
moduleOutputKind = nil
}
Expand All @@ -2011,7 +2020,9 @@ extension Driver {
var moduleNameIsFallback = false
if let arg = parsedOptions.getLastArgument(.moduleName) {
moduleName = arg.asSingle
} else if compilerMode == .repl {
} else if compilerMode == .repl || compilerMode == .intro {
// TODO: Remove the `.intro` check once the REPL no longer launches
// by default.
// REPL mode should always use the REPL module.
moduleName = "REPL"
} else if let outputArg = parsedOptions.getLastArgument(.o) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ fileprivate extension CompilerMode {
var supportsIncrementalCompilation: Bool {
switch self {
case .standardCompile, .immediate, .repl, .batchCompile: return true
case .singleCompile, .compilePCM, .dumpPCM: return false
case .singleCompile, .compilePCM, .dumpPCM, .intro: return false
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion Sources/SwiftDriver/Jobs/FrontendJobHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ extension Driver {
commandLine.appendFlag(.target)
commandLine.appendFlag(targetTriple.triple)
}
case .intro:
break
}

// Pass down -clang-target.
Expand Down Expand Up @@ -270,7 +272,7 @@ extension Driver {
}

// Repl Jobs shouldn't include -module-name.
if compilerMode != .repl {
if compilerMode != .repl && compilerMode != .intro {
commandLine.appendFlags("-module-name", moduleOutputInfo.name)
}

Expand Down
16 changes: 15 additions & 1 deletion Sources/SwiftDriver/Jobs/Planning.swift
Original file line number Diff line number Diff line change
Expand Up @@ -699,7 +699,7 @@ extension Driver {
}

// The REPL doesn't require input files, but all other modes do.
guard !inputFiles.isEmpty || compilerMode == .repl else {
guard !inputFiles.isEmpty || compilerMode == .repl || compilerMode == .intro else {
if parsedOptions.hasArgument(.v) {
// `swiftc -v` is allowed and prints version information.
return ([], nil)
Expand Down Expand Up @@ -735,6 +735,20 @@ extension Driver {
throw PlanningError.dumpPCMWrongInputFiles
}
return ([try generateDumpPCMJob(input: inputFiles.first!)], nil)
case .intro:
// TODO: Remove this check once the REPL no longer launches
// by default.
if !inputFiles.isEmpty {
throw PlanningError.replReceivedInput
}
// end TODO.

var introJobs = try helpIntroJobs()

// TODO: Remove this to stop launching the REPL by default.
introJobs.append(try replJob())

return (introJobs, nil)
}
}
}
Expand Down
38 changes: 38 additions & 0 deletions Sources/SwiftDriver/Jobs/SwiftHelpIntroJob.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===--------------- SwiftHelpIntroJob.swift - Swift REPL -----------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SwiftOptions

extension Driver {
mutating func helpIntroJobs() throws -> [Job] {
return [
Job(
moduleName: moduleOutputInfo.name,
kind: .versionRequest,
tool: .absolute(try toolchain.getToolPath(.swiftCompiler)),
commandLine: [.flag("--version")],
inputs: [],
primaryInputs: [],
outputs: [],
requiresInPlaceExecution: false),
Job(
moduleName: moduleOutputInfo.name,
kind: .help,
tool: .absolute(try toolchain.getToolPath(.swiftHelp)),
commandLine: [.flag("intro")],
inputs: [],
primaryInputs: [],
outputs: [],
requiresInPlaceExecution: false
),
]
}
}
72 changes: 60 additions & 12 deletions Sources/swift-help/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ import ArgumentParser
enum HelpTopic: ExpressibleByArgument, CustomStringConvertible {
case driver(DriverKind)
case subcommand(Subcommand)
case intro

init?(argument topicName: String) {
if let kind = DriverKind(rawValue: topicName) {
self = .driver(kind)
} else if let subcommand = Subcommand(rawValue: topicName) {
self = .subcommand(subcommand)
} else if topicName == "intro" {
self = .intro
} else {
return nil
}
Expand All @@ -33,6 +36,8 @@ enum HelpTopic: ExpressibleByArgument, CustomStringConvertible {
return kind.rawValue
case .subcommand(let command):
return command.rawValue
case .intro:
return "intro"
}
}
}
Expand All @@ -43,13 +48,13 @@ enum Subcommand: String, CaseIterable {
var description: String {
switch self {
case .build:
return "SwiftPM - Build sources into binary products"
return "Build Swift packages"
case .package:
return "SwiftPM - Perform operations on Swift packages"
return "Create and work on packages"
case .run:
return "SwiftPM - Build and run an executable product"
return "Run a program from a package"
case .test:
return "SwiftPM - Build and run tests"
return "Run package tests"
}
}
}
Expand All @@ -65,19 +70,60 @@ struct SwiftHelp: ParsableCommand {
help: "List hidden (unsupported) options")
var showHidden: Bool = false

enum Color256: CustomStringConvertible {
case reset
case color(foreground: UInt8?, background: UInt8?)

var description: String {
switch self {
case .reset:
return "\u{001B}[0m"
case let .color(foreground, background):
let foreground = foreground.map { "\u{001B}[38;5;\($0)m" } ?? ""
let background = background.map { "\u{001B}[48;5;\($0)m" } ?? ""
return foreground + background
}
}
}

func printIntro() {
let is256Color = ProcessEnv.vars["TERM"] == "xterm-256color"
let orangeRed = is256Color ? "\u{001b}[1;38;5;196m" : ""
let plain = is256Color ? "\u{001b}[0m" : ""
let plainBold = is256Color ? "\u{001b}[1m" : ""

print("""

\(orangeRed)Welcome to Swift!\(plain)

\(plainBold)Subcommands:\(plain)

""")

let maxSubcommandNameLength = Subcommand.allCases.map { $0.rawValue.count }.max()!

for command in Subcommand.allCases {
let padding = String(repeating: " ", count: maxSubcommandNameLength - command.rawValue.count)
print(" \(plainBold)swift \(command.rawValue)\(plain)\(padding) \(command.description)")
}

// `repl` not included in `Subcommand`, also print it here.
do {
let padding = String(repeating: " ", count: maxSubcommandNameLength - "repl".count)
print(" \(plainBold)swift repl\(plain)\(padding) Experiment with Swift code interactively (default)")
}

print("\n Use \(plainBold)`swift --help`\(plain) for descriptions of available options and flags.")
print("\n Use \(plainBold)`swift help <subcommand>`\(plain) for more information about a subcommand.")
print()
}

func run() throws {
let driverOptionTable = OptionTable()
switch topic {
case .driver(let kind):
driverOptionTable.printHelp(driverKind: kind, includeHidden: showHidden)
print("\nSUBCOMMANDS (swift <subcommand> [arguments]):")
let maxSubcommandNameLength = Subcommand.allCases.map(\.rawValue.count).max()!
for subcommand in Subcommand.allCases {
let padding = String(repeating: " ", count: maxSubcommandNameLength - subcommand.rawValue.count)
print(" \(subcommand.rawValue):\(padding) \(subcommand.description)")
}
print("\n Use \"swift help <subcommand>\" for more information about a subcommand")

printIntro()
case .subcommand(let subcommand):
// Try to find the subcommand adjacent to the help tool.
// If we didn't find the tool there, let the OS search for it.
Expand All @@ -98,6 +144,8 @@ struct SwiftHelp: ParsableCommand {
} else {
try exec(path: path.pathString, args: [execName, "help"] + subtopics)
}
case .intro:
printIntro()
}
}
}
Expand Down
Loading