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
|
//===----------------------------------------------------------------------===//
//
// 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 LSPLogging
import LanguageServerProtocol
import SwiftRefactor
import SwiftSyntax
/// Describes types that provide one or more code actions based on purely
/// syntactic information.
protocol SyntaxCodeActionProvider {
/// Produce code actions within the given scope. Each code action
/// corresponds to one syntactic transformation that can be performed, such
/// as adding or removing separators from an integer literal.
static func codeActions(in scope: SyntaxCodeActionScope) -> [CodeAction]
}
/// Defines the scope in which a syntactic code action occurs.
struct SyntaxCodeActionScope {
/// The snapshot of the document on which the code actions will be evaluated.
var snapshot: DocumentSnapshot
/// The actual code action request, which can specify additional parameters
/// to guide the code actions.
var request: CodeActionRequest
/// The source file in which the syntactic code action will operate.
var file: SourceFileSyntax
/// The UTF-8 byte range in the source file in which code actions should be
/// considered, i.e., where the cursor or selection is.
var range: Range<AbsolutePosition>
/// The innermost node that contains the entire selected source range
var innermostNodeContainingRange: Syntax?
init?(
snapshot: DocumentSnapshot,
syntaxTree file: SourceFileSyntax,
request: CodeActionRequest
) {
self.snapshot = snapshot
self.request = request
self.file = file
guard let left = tokenForRefactoring(at: request.range.lowerBound, snapshot: snapshot, syntaxTree: file),
let right = tokenForRefactoring(at: request.range.upperBound, snapshot: snapshot, syntaxTree: file)
else {
return nil
}
self.range = left.position..<right.endPosition
self.innermostNodeContainingRange = findCommonAncestorOrSelf(Syntax(left), Syntax(right))
}
}
private func tokenForRefactoring(
at position: Position,
snapshot: DocumentSnapshot,
syntaxTree: SourceFileSyntax
) -> TokenSyntax? {
let absolutePosition = snapshot.absolutePosition(of: position)
if absolutePosition == syntaxTree.endPosition {
// token(at:) will not find the end of file token if the end of file token has length 0. Special case this and
// return the last proper token in this case.
return syntaxTree.endOfFileToken.previousToken(viewMode: .sourceAccurate)
}
guard let token = syntaxTree.token(at: absolutePosition) else {
return nil
}
// See `adjustPositionToStartOfIdentifier`. We need to be a little more aggressive for the refactorings and also
// adjust to the start of punctuation eg. if the end of the selected range is after a `}`, we want the end token for
// the refactoring to be the `}`, not the token after `}`.
if absolutePosition == token.position,
let previousToken = token.previousToken(viewMode: .sourceAccurate),
previousToken.endPositionBeforeTrailingTrivia == absolutePosition
{
return previousToken
}
return token
}
|