Skip to content

Add import extraction support to Structure#838

Open
dalemyers wants to merge 1 commit into
jpsim:mainfrom
dalemyers:structure_add_imports
Open

Add import extraction support to Structure#838
dalemyers wants to merge 1 commit into
jpsim:mainfrom
dalemyers:structure_add_imports

Conversation

@dalemyers

@dalemyers dalemyers commented Mar 14, 2026

Copy link
Copy Markdown

Summary

  • Adds ImportInfo type that extracts import statements from SourceKit syntax maps, including support for @testable/@_exported attributes, submodule imports (e.g. Foundation.NSObject), and kind-qualified imports (e.g. import class Foundation.NSObject)
  • Extends Structure with an extractImports parameter that populates a key.imports array in the structure dictionary
  • Adds --imports flag to the sourcekitten structure CLI command

Details

Import information is currently lost when Structure strips the syntax map from the SourceKit response. This change optionally preserves it by extracting imports before discarding the syntax map.

The feature is opt-in (extractImports: false by default) so there are no changes to existing behavior.

Each import entry includes:

  • key.name — full import path (e.g. Foundation.NSObject)
  • key.offset / key.length — byte range covering the full statement including attributes
  • key.attributes — array of attributes like @testable with their own offsets/lengths
  • key.import_kind — declaration kind for qualified imports (class, struct, func, etc.)
  • key.module_name — first path component for dotted imports

Example

demo.swift:

import Foundation
import UIKit
@testable import MyAppCore
@_exported import Logging
import struct Foundation.URL
import class Foundation.NSObject
import func Darwin.exit
import Foundation.NSObject
import enum Dispatch.DispatchQoS

struct App {
    let name: String
    let version: String

    func run() {
        print("Running \(name) v\(version)")
    }
}

Output:

{
  "key.diagnostic_stage" : "source.diagnostic.stage.swift.parse",
  "key.imports" : [
    {
      "key.length" : 17,
      "key.name" : "Foundation",
      "key.offset" : 0
    },
    {
      "key.length" : 12,
      "key.name" : "UIKit",
      "key.offset" : 18
    },
    {
      "key.attributes" : [
        {
          "key.attribute" : "source.decl.attribute.testable",
          "key.length" : 9,
          "key.offset" : 31
        }
      ],
      "key.length" : 26,
      "key.name" : "MyAppCore",
      "key.offset" : 31
    },
    {
      "key.attributes" : [
        {
          "key.attribute" : "source.decl.attribute._exported",
          "key.length" : 10,
          "key.offset" : 58
        }
      ],
      "key.length" : 25,
      "key.name" : "Logging",
      "key.offset" : 58
    },
    {
      "key.import_kind" : "struct",
      "key.length" : 28,
      "key.module_name" : "Foundation",
      "key.name" : "Foundation.URL",
      "key.offset" : 84
    },
    {
      "key.import_kind" : "class",
      "key.length" : 32,
      "key.module_name" : "Foundation",
      "key.name" : "Foundation.NSObject",
      "key.offset" : 113
    },
    {
      "key.import_kind" : "func",
      "key.length" : 23,
      "key.module_name" : "Darwin",
      "key.name" : "Darwin.exit",
      "key.offset" : 146
    },
    {
      "key.length" : 26,
      "key.module_name" : "Foundation",
      "key.name" : "Foundation.NSObject",
      "key.offset" : 170
    },
    {
      "key.import_kind" : "enum",
      "key.length" : 32,
      "key.module_name" : "Dispatch",
      "key.name" : "Dispatch.DispatchQoS",
      "key.offset" : 197
    }
  ],
  "key.length" : 359,
  "key.offset" : 0,
  "key.substructure" : [
    {
      "key.accessibility" : "source.lang.swift.accessibility.internal",
      "key.bodylength" : 115,
      "key.bodyoffset" : 243,
      "key.kind" : "source.lang.swift.decl.struct",
      "key.length" : 128,
      "key.name" : "App",
      "key.namelength" : 3,
      "key.nameoffset" : 238,
      "key.offset" : 231,
      "key.substructure" : [
        {
          "key.accessibility" : "source.lang.swift.accessibility.internal",
          "key.kind" : "source.lang.swift.decl.var.instance",
          "key.length" : 16,
          "key.name" : "name",
          "key.namelength" : 4,
          "key.nameoffset" : 252,
          "key.offset" : 248,
          "key.typename" : "String"
        },
        {
          "key.accessibility" : "source.lang.swift.accessibility.internal",
          "key.kind" : "source.lang.swift.decl.var.instance",
          "key.length" : 19,
          "key.name" : "version",
          "key.namelength" : 7,
          "key.nameoffset" : 273,
          "key.offset" : 269,
          "key.typename" : "String"
        },
        {
          "key.accessibility" : "source.lang.swift.accessibility.internal",
          "key.bodylength" : 50,
          "key.bodyoffset" : 306,
          "key.kind" : "source.lang.swift.decl.function.method.instance",
          "key.length" : 63,
          "key.name" : "run()",
          "key.namelength" : 5,
          "key.nameoffset" : 299,
          "key.offset" : 294,
          "key.substructure" : [
            {
              "key.bodylength" : 29,
              "key.bodyoffset" : 321,
              "key.kind" : "source.lang.swift.expr.call",
              "key.length" : 36,
              "key.name" : "print",
              "key.namelength" : 5,
              "key.nameoffset" : 315,
              "key.offset" : 315,
              "key.substructure" : [
                {
                  "key.bodylength" : 29,
                  "key.bodyoffset" : 321,
                  "key.kind" : "source.lang.swift.expr.argument",
                  "key.length" : 29,
                  "key.offset" : 321
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

@johnfairh

johnfairh commented Mar 21, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the PR; I think the best way for applications to get this info is, instead of using SourceKitten, to use SwiftSyntax on the file - this is guaranteed to parse correctly forever and it should be easier to break down attribute syntax. An alternative would be go back to SourceKit and teach it how to emit structure nodes for import statements -- ImportDecls are just skipped over today.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants