Skip to content

Commit b35fbe2

Browse files
authored
Merge pull request #245 from tayloraswift/indexstore-highlighting
improved indexstore-powered highlights
2 parents 5f2c97e + a9cd307 commit b35fbe2

17 files changed

+318
-29
lines changed

.github/pipeline

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
#!/bin/bash
22
set -e
3+
34
swift --version
4-
swift build -c release --explicit-target-dependency-import-check=error
5+
6+
swift build -c release \
7+
--explicit-target-dependency-import-check=error \
8+
-Xcxx -I/usr/lib/swift \
9+
-Xcxx -I/usr/lib/swift/Block
10+
511
./generate-test-symbolgraphs
612
for f in .build/release/*Tests; do
713
$f

.github/workflows/ci.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,17 @@ jobs:
5454
- name: Build debug
5555
run: |
5656
swift --version
57-
swift build
57+
swift build -Xcxx -I/usr/lib/swift -Xcxx -I/usr/lib/swift/Block
5858
5959
- name: Build release
6060
run: |
61-
swift build -c release
61+
swift build -c release \
62+
-Xcxx -I/usr/lib/swift \
63+
-Xcxx -I/usr/lib/swift/Block
6264
6365
- name: Test SymbolGraphBuilder
6466
run: |
65-
swift run -c release SymbolGraphBuilderTests
67+
swift run -c release \
68+
-Xcxx -I/usr/lib/swift \
69+
-Xcxx -I/usr/lib/swift/Block \
70+
SymbolGraphBuilderTests

Package.resolved

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"originHash" : "25883f12cf4eb759ed4a7b02bac9272cad664cd8aabefb3cc5928b2793c7845f",
2+
"originHash" : "11a2c92dc12e2d1090f389f23c98adcb98ad3e0b6ccf1e724d65aeb0152c5554",
33
"pins" : [
44
{
55
"identity" : "swift-atomics",

Package.swift

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// swift-tools-version:5.10
2+
import class Foundation.ProcessInfo
23
import PackageDescription
34
import CompilerPluginSupport
45

56
let package:Package = .init(
6-
name: "swift-unidoc",
7+
name: "Swift Unidoc",
78
platforms: [.macOS(.v14)],
89
products: [
910
.executable(name: "ssgc", targets: ["ssgc"]),
@@ -102,9 +103,6 @@ let package:Package = .init(
102103
.package(url: "https://github.com/tayloraswift/swift-png", .upToNextMinor(
103104
from: "4.4.2")),
104105

105-
// .package(url: "https://github.com/apple/indexstore-db",
106-
// branch: "swift-5.10-RELEASE"),
107-
108106
.package(url: "https://github.com/apple/swift-atomics", .upToNextMinor(
109107
from: "1.2.0")),
110108
.package(url: "https://github.com/apple/swift-collections.git", .upToNextMinor(
@@ -296,18 +294,13 @@ let package:Package = .init(
296294
.target(name: "MarkdownABI"),
297295
.target(name: "Signatures"),
298296
.target(name: "Snippets"),
297+
.target(name: "Sources"),
299298
.target(name: "Symbols"),
300299

301300
.product(name: "SwiftIDEUtils", package: "swift-syntax"),
302301
.product(name: "SwiftParser", package: "swift-syntax"),
303302
]),
304303

305-
.target(name: "MarkdownPluginSwift_IndexStoreDB",
306-
dependencies: [
307-
.target(name: "MarkdownPluginSwift"),
308-
// .product(name: "IndexStoreDB", package: "indexstore-db"),
309-
]),
310-
311304
.target(name: "MarkdownSemantics",
312305
dependencies: [
313306
.target(name: "MarkdownAST"),
@@ -756,6 +749,25 @@ let package:Package = .init(
756749
.target(name: "guides", path: "Guides"),
757750
])
758751

752+
switch ProcessInfo.processInfo.environment["UNIDOC_ENABLE_INDEXSTORE"]?.lowercased()
753+
{
754+
case "1"?, "true"?:
755+
package.dependencies.append(.package(url: "https://github.com/apple/indexstore-db",
756+
branch: "swift-5.10-RELEASE"))
757+
758+
package.targets.append(.target(name: "MarkdownPluginSwift_IndexStoreDB",
759+
dependencies: [
760+
.target(name: "MarkdownPluginSwift"),
761+
.product(name: "IndexStoreDB", package: "indexstore-db"),
762+
]))
763+
764+
default:
765+
package.targets.append(.target(name: "MarkdownPluginSwift_IndexStoreDB",
766+
dependencies: [
767+
.target(name: "MarkdownPluginSwift"),
768+
]))
769+
}
770+
759771
for target:PackageDescription.Target in package.targets
760772
{
761773
if target.name == "_AsyncChannel"
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import Symbols
2+
import Sources
3+
4+
extension Markdown.SwiftLanguage
5+
{
6+
@frozen public
7+
struct IndexMarker
8+
{
9+
public
10+
let position:SourcePosition
11+
public
12+
let symbol:Symbol.USR
13+
public
14+
let phylum:Phylum.Decl?
15+
16+
@inlinable public
17+
init(position:SourcePosition, symbol:Symbol.USR, phylum:Phylum.Decl?)
18+
{
19+
self.position = position
20+
self.symbol = symbol
21+
self.phylum = phylum
22+
}
23+
}
24+
}
25+
extension Markdown.SwiftLanguage.IndexMarker:CustomStringConvertible
26+
{
27+
public
28+
var description:String
29+
{
30+
"(\(self.position): \(self.symbol), \(self.phylum?.name ?? "unknown"))"
31+
}
32+
}

Sources/MarkdownPluginSwift/Markdown.SwiftLanguage.IndexStore.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ extension Markdown.SwiftLanguage
33
public
44
protocol IndexStore:AnyObject
55
{
6-
func load(for path:String)
6+
/// Returns a list of index markers, indexed by UTF-8 offset.
7+
func load(for path:String, utf8:[UInt8]) -> [Int: IndexMarker]
78
}
89
}

Sources/MarkdownPluginSwift/Markdown.SwiftLanguage.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,16 @@ extension Markdown.SwiftLanguage
3434
func parse(snippet utf8:[UInt8],
3535
from indexID:String? = nil) -> (caption:String, slices:[Markdown.SnippetSlice])
3636
{
37-
if let indexID:String
37+
let links:[Int: IndexMarker]
38+
39+
if let indexID:String,
40+
let index:any IndexStore = self.index
41+
{
42+
links = index.load(for: indexID, utf8: utf8)
43+
}
44+
else
3845
{
39-
self.index?.load(for: indexID)
46+
links = [:]
4047
}
4148

4249
// It is safe to escape the pointer to ``Parser.parse(source:maximumNestingLevel:)``,
@@ -98,7 +105,7 @@ extension Markdown.SwiftLanguage
98105
}
99106

100107
let slices:[SnippetParser.Slice] = parser.finish(at: parsed.endPosition, in: utf8)
101-
var cursor:SyntaxClassificationCursor = .init(parsed.classifications)
108+
var cursor:SyntaxClassificationCursor = .init(parsed.classifications, links: links)
102109

103110
let rendered:[Markdown.SnippetSlice] = slices.map
104111
{
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import SwiftIDEUtils
2+
import Symbols
3+
4+
extension SyntaxClassificationCursor
5+
{
6+
struct SpanIterator
7+
{
8+
private
9+
var links:[Int: Markdown.SwiftLanguage.IndexMarker]
10+
private
11+
var spans:Array<SyntaxClassifiedRange>.Iterator
12+
13+
init(_ spans:consuming SyntaxClassifications,
14+
links:[Int: Markdown.SwiftLanguage.IndexMarker] = [:])
15+
{
16+
self.links = links
17+
self.spans = spans.makeIterator()
18+
}
19+
}
20+
}
21+
extension SyntaxClassificationCursor.SpanIterator:CustomDebugStringConvertible
22+
{
23+
var debugDescription:String
24+
{
25+
self.links
26+
.sorted
27+
{
28+
$0.key < $1.key
29+
}
30+
.map
31+
{
32+
"[\($0.key)]: \($0.value)"
33+
}
34+
.joined(separator: "\n")
35+
}
36+
}
37+
extension SyntaxClassificationCursor.SpanIterator
38+
{
39+
mutating
40+
func next() -> SyntaxClassifiedRange?
41+
{
42+
guard
43+
var highlight:SyntaxClassifiedRange = self.spans.next()
44+
else
45+
{
46+
return nil
47+
}
48+
49+
if let marker:Markdown.SwiftLanguage.IndexMarker = self.links[highlight.offset],
50+
let phylum:Phylum.Decl = marker.phylum
51+
{
52+
switch phylum
53+
{
54+
case .actor: highlight.kind = .type
55+
case .associatedtype: highlight.kind = .type
56+
case .class: highlight.kind = .type
57+
case .enum: highlight.kind = .type
58+
case .protocol: highlight.kind = .type
59+
case .struct: highlight.kind = .type
60+
case .typealias: highlight.kind = .type
61+
case .func: highlight.kind = .identifier
62+
case .initializer: highlight.kind = .keyword
63+
default: break
64+
}
65+
}
66+
67+
return highlight
68+
}
69+
}

Sources/MarkdownPluginSwift/SyntaxClassificationCursor.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ import SwiftSyntax
33

44
struct SyntaxClassificationCursor
55
{
6-
var spans:SyntaxClassifications.Iterator
6+
private(set)
7+
var spans:SpanIterator
8+
private
79
var span:SyntaxClassifiedRange?
810

9-
init(_ spans:consuming SyntaxClassifications)
11+
init(_ spans:consuming SyntaxClassifications,
12+
links:[Int: Markdown.SwiftLanguage.IndexMarker] = [:])
1013
{
11-
self.spans = spans.makeIterator()
14+
self.spans = .init(spans, links: links)
1215
self.span = self.spans.next()
1316
}
1417

Sources/MarkdownPluginSwift_IndexStoreDB/IndexStoreDB (ext).swift

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,89 @@
11
#if canImport(IndexStoreDB)
22

3-
import IndexStoreDB
3+
import class IndexStoreDB.IndexStoreDB
44
import MarkdownPluginSwift
5+
import Sources
6+
import Symbols
57

68
extension IndexStoreDB:Markdown.SwiftLanguage.IndexStore
79
{
810
public
9-
func load(for id:String)
11+
func load(for id:String, utf8:[UInt8]) -> [Int: Markdown.SwiftLanguage.IndexMarker]
1012
{
11-
for symbol:Symbol in self.symbols(inFilePath: id)
13+
// Compute line positions
14+
var lines:[Int: Int] = [1: utf8.startIndex]
15+
var line:Int = 1
16+
for (i, byte):(Int, UInt8) in zip(utf8.indices, utf8)
1217
{
13-
for occurence:SymbolOccurrence in self.occurrences(ofUSR: symbol.usr, roles: .all)
18+
if byte == 0x0A
1419
{
15-
print(occurence)
20+
line += 1
21+
lines[line] = utf8.index(after: i)
1622
}
1723
}
24+
25+
var markers:[Int: Markdown.SwiftLanguage.IndexMarker] = [:]
26+
for symbol:IndexStoreDB.Symbol_ in self.symbols(inFilePath: id)
27+
{
28+
for occurence:IndexStoreDB.SymbolOccurrence_ in self.occurrences(ofUSR: symbol.usr,
29+
roles: .all)
30+
{
31+
guard
32+
let position:SourcePosition = .init(line: occurence.location.line - 1,
33+
column: occurence.location.utf8Column - 1),
34+
let base:Int = lines[occurence.location.line],
35+
let usr:Symbol.USR = .init(symbol.usr)
36+
else
37+
{
38+
continue
39+
}
40+
41+
let phylum:Phylum.Decl?
42+
43+
switch symbol.kind
44+
{
45+
case .constructor:
46+
phylum = occurence.roles.contains(.call)
47+
? .func(.static)
48+
: .initializer
49+
50+
case .unknown: phylum = nil
51+
case .module: phylum = nil
52+
case .namespace: phylum = nil
53+
case .namespaceAlias: phylum = nil
54+
case .macro: phylum = nil
55+
case .enum: phylum = .enum
56+
case .struct: phylum = .struct
57+
case .class: phylum = .class
58+
case .protocol: phylum = .protocol
59+
case .extension: phylum = .typealias
60+
case .union: phylum = .enum
61+
case .typealias: phylum = .typealias
62+
case .function: phylum = .func(nil)
63+
case .variable: phylum = .var(nil)
64+
case .field: phylum = nil
65+
case .enumConstant: phylum = .case
66+
case .instanceMethod: phylum = .func(.instance)
67+
case .classMethod: phylum = .func(.class)
68+
case .staticMethod: phylum = .func(.static)
69+
case .instanceProperty: phylum = .var(.instance)
70+
case .classProperty: phylum = .var(.class)
71+
case .staticProperty: phylum = .var(.static)
72+
case .destructor: phylum = .deinitializer
73+
case .conversionFunction: phylum = nil
74+
case .parameter: phylum = nil
75+
case .using: phylum = nil
76+
case .concept: phylum = nil
77+
case .commentTag: phylum = nil
78+
}
79+
80+
markers[base + occurence.location.utf8Column - 1] = .init(position: position,
81+
symbol: usr,
82+
phylum: phylum)
83+
}
84+
}
85+
86+
return markers
1887
}
1988
}
2089

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#if canImport(IndexStoreDB)
2+
3+
import IndexStoreDB
4+
5+
extension IndexStoreDB
6+
{
7+
typealias Symbol_ = Symbol
8+
typealias SymbolOccurrence_ = SymbolOccurrence
9+
}
10+
11+
#endif

0 commit comments

Comments
 (0)