File: AddDocumentation.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (152 lines) | stat: -rw-r--r-- 4,984 bytes parent folder | download
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 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 SwiftBasicFormat
import SwiftParser
import SwiftRefactor
import SwiftSyntax

/// Insert a documentation template associated with a function or macro.
///
/// ## Before
///
/// ```swift
/// static func refactor(syntax: DeclSyntax, in context: Void) -> DeclSyntax? {}
/// ```
///
/// ## After
///
/// ```swift
/// ///
/// /// - Parameters:
/// ///   - syntax:
/// ///   - context:
/// /// - Returns:
/// static func refactor(syntax: DeclSyntax, in context: Void) -> DeclSyntax? {}
/// ```
@_spi(Testing)
public struct AddDocumentation: EditRefactoringProvider {
  @_spi(Testing)
  public static func textRefactor(syntax: DeclSyntax, in context: Void) -> [SourceEdit] {
    let hasDocumentation = syntax.leadingTrivia.contains { trivia in
      switch trivia {
      case .blockComment, .docBlockComment, .lineComment, .docLineComment:
        return true
      default:
        return false
      }
    }

    // We consider nodes at the start of the source file at being on a new line
    let isOnNewLine =
      syntax.leadingTrivia.contains(where: \.isNewline) || syntax.previousToken(viewMode: .sourceAccurate) == nil

    guard !hasDocumentation && isOnNewLine else {
      return []
    }

    let newlineAndIndentation = [.newlines(1)] + (syntax.firstToken(viewMode: .sourceAccurate)?.indentationOfLine ?? [])
    var content: [TriviaPiece] = []
    content.append(.docLineComment("/// A description"))

    if let parameters = syntax.parameters?.parameters {
      if let onlyParam = parameters.only {
        let paramToken = onlyParam.secondName?.text ?? onlyParam.firstName.text
        content += newlineAndIndentation
        content.append(.docLineComment("/// - Parameter \(paramToken):"))
      } else {
        content += newlineAndIndentation
        content.append(.docLineComment("/// - Parameters:"))
        content += parameters.flatMap({ param in
          newlineAndIndentation + [
            .docLineComment("///   - \(param.secondName?.text ?? param.firstName.text):")
          ]
        })
        content += newlineAndIndentation
        content.append(.docLineComment("///"))
      }
    }

    if syntax.throwsKeyword != nil {
      content += newlineAndIndentation
      content.append(.docLineComment("/// - Throws:"))
    }

    if syntax.returnType != nil {
      content += newlineAndIndentation
      content.append(.docLineComment("/// - Returns:"))
    }
    content += newlineAndIndentation

    let insertPos = syntax.positionAfterSkippingLeadingTrivia
    return [
      SourceEdit(
        range: insertPos..<insertPos,
        replacement: Trivia(pieces: content).description
      )
    ]
  }
}

extension AddDocumentation: SyntaxRefactoringCodeActionProvider {
  static func nodeToRefactor(in scope: SyntaxCodeActionScope) -> Input? {
    return scope.innermostNodeContainingRange?.findParentOfSelf(
      ofType: DeclSyntax.self,
      stoppingIf: { $0.is(CodeBlockItemSyntax.self) || $0.is(MemberBlockItemSyntax.self) || $0.is(ExprSyntax.self) }
    )
  }

  static var title: String { "Add documentation" }
}

extension DeclSyntax {
  fileprivate var parameters: FunctionParameterClauseSyntax? {
    switch self.as(DeclSyntaxEnum.self) {
    case .functionDecl(let functionDecl):
      return functionDecl.signature.parameterClause
    case .subscriptDecl(let subscriptDecl):
      return subscriptDecl.parameterClause
    case .initializerDecl(let initializer):
      return initializer.signature.parameterClause
    case .macroDecl(let macro):
      return macro.signature.parameterClause
    default:
      return nil
    }
  }

  fileprivate var throwsKeyword: TokenSyntax? {
    switch self.as(DeclSyntaxEnum.self) {
    case .functionDecl(let functionDecl):
      return functionDecl.signature.effectSpecifiers?.throwsClause?.throwsSpecifier
    case .initializerDecl(let initializer):
      return initializer.signature.effectSpecifiers?.throwsClause?.throwsSpecifier
    default:
      return nil
    }
  }

  fileprivate var returnType: TypeSyntax? {
    switch self.as(DeclSyntaxEnum.self) {
    case .functionDecl(let functionDecl):
      return functionDecl.signature.returnClause?.type
    case .subscriptDecl(let subscriptDecl):
      return subscriptDecl.returnClause.type
    case .initializerDecl(let initializer):
      return initializer.signature.returnClause?.type
    case .macroDecl(let macro):
      return macro.signature.returnClause?.type
    default:
      return nil
    }
  }
}