[GSoC 2019] Code Formatting Functionality for SourceKit-LSP

gsoc-2019
(Muhammad Salman Zafar) #21

I was also getting the CCommonMark error and how i solved the error

if you look into swift-format package.swift file

  targets: [
    .target(
      name: "CCommonMark",
      exclude: [
        "cmark/api_test",
        // We must exclude main.c or SwiftPM will treat this target as an executable target instead
        // of a library, and we won't be able to import it from the CommonMark Swift module.
        "cmark/src/main.c",
        "cmark/test",
      ]

They are using the cmark so i downloaded the cmark and added the downloaded file to ./swift-format/Sources/CCommonMark/cmark

Then swift build
Then swift package generate-xcodeproj to generate xcode file
Then CMD+R

and in debug folder you will find the swift-format file

./swift-format -v to display the version

or if you go to main.swift file there is a function main (at the end of the file) taking argument of string array

exit(main(["swift-format", "-v"])) this will also display the current version

(Nick) #22

CCommonMark is a C library, so it can't be added to the SPM. Instead, the devs have it listed as a submodule of the project. After cloning swift-format, you can initialize and download CCommonMark by running these two commands from the project directory:

git submodule init
git submodule update
(Nick) #23

@allevato I've woke up to snow and temperatures in the teens the last couple of mornings. Good ole southwest Virginia for you. I have a couple of friends/family going to Virginia Tech at the moment. It's a beautiful campus and Blacksburg is a great city!

I've been looking through the SwiftFormat module of swift-format, and I believe I have a decent grasp of what is going on.

Essentially, we need to overload the format method of SwiftFormatter to accept a Range parameter that would need properties for the beginning line/character and ending line/character.

The first approach that comes to mind is to grab the code from the given range, create a temporary file containing the code, parse that into a syntax tree, and then pass it to format<Output: TextOutputStream>(syntax: Syntax, assumingFileURL url: URL, to outputStream: inout Output). I was trying to come up with a way of doing it without creating a temporary file, but it seems as if most of the methods I would be calling require a URL. Am I on the right track with this line of thinking?

On another note, I was attempting to use the format-diff.sh bash script to see swift-format in action, but I'm getting error: unknown option -p; use --help to list available options when I invoke the script. The bash script is trying to pass a -p option to swift-format even though it doesn't accept one.

(Tony Allevato) #24

The SwiftSyntax folks are better equipped to provide more details here (@akyrtzi). Since swift-format uses the 4.2-compatible APIs, it works by invoking swiftc under the hood so you would need to create a temporary in that case. More recent versions have other incremental parsing functionality that might be useful here to get you a partial Syntax tree that you can pass in instead, but I'm not sure what the state of those APIs is.

Oops, thanks for pointing that out! The SwiftFormatPrettyPrint phase of the formatter was originally optional until it was fully baked, and that was enabled by passing -p, but now it's enabled by default and that flag was removed. I'll update the script.

I'll also update the README to make sure this is more obvious.

1 Like
(Argyrios Kyrtzidis) #25

You don't need to parse again in order to get the syntax tree of a given range, you already have this tree, it is contained within the syntax tree of the whole file.

To clarify more, if you extract the source code of the range, the given range may not result in valid source code. You should assume you have the whole tree of the whole file and then asked to only re-format a range from it.

2 Likes
(Muhammad Salman Zafar) #26

i have taken the specific syntax node and pass only that specific node instead of the whole tree

public init(context: Context, node: Syntax, printTokenStream: Bool) {

but how can i get the specific node in more complex tree, it only work with this swift file

swift file

print(      "hello world"       )
print("syntax tree")

var hello =             "hello world"

Swift-Formatter

print("Enter the line: ", terminator: "")
    if let line = Int(readLine() ?? "") {
        if let node = getNode(transformedSyntax, line) {
            prettyPrint(context: context, node: node, debugOptions: debugOptions)
        }
    }

    func getNode( _ syntax: Syntax ,_ line: Int) -> Syntax? {

    guard let syntaxChild = syntax.child(at: 0) else { return nil }
    guard let child = syntaxChild.child(at: line) else { return nil }

    return child
    }

    func prettyPrint(context: Context, node: Syntax, debugOptions: DebugOptions) {

    let printer = PrettyPrinter(
        context: context,
        node: node,
        printTokenStream: debugOptions.contains(.dumpTokenStream))

        print(printer.prettyPrint())   
    }
(Argyrios Kyrtzidis) #27

child(at:) accepts the index for the node child to return (e.g. for a function call argument list, '2' will return the third argument), it doesn't accept source line numbers.
You would find the sub-nodes of a range by walking the tree and comparing the source positions of the nodes in relation to the given source range.

A good way to explore the SwiftSyntax tree of some source code is to run

swift -frontend -emit-syntax /path/to/file.swift | python -m json.tool

or use this SwiftSyntax AST Explorer site.

1 Like
(Argyrios Kyrtzidis) split this topic #28

A post was split to a new topic: Getting SwiftSyntax subtree using source range