Skip to content

Commit 08c531c

Browse files
committed
basic implementation for rendering snippets
1 parent 2527649 commit 08c531c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+615
-425
lines changed

Package.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ let package:Package = .init(
320320
[
321321
.target(name: "MarkdownABI"),
322322
.target(name: "Signatures"),
323+
.target(name: "Snippets"),
323324
.target(name: "Symbols"),
324325

325326
.product(name: "SwiftIDEUtils", package: "swift-syntax"),
@@ -391,6 +392,11 @@ let package:Package = .init(
391392
.target(name: "DOM"),
392393
]),
393394

395+
.target(name: "Snippets", dependencies:
396+
[
397+
.target(name: "MarkdownABI"),
398+
]),
399+
394400
.target(name: "Sources"),
395401

396402
.target(name: "Swiftinit", dependencies:
@@ -463,6 +469,7 @@ let package:Package = .init(
463469
.target(name: "MarkdownSemantics"),
464470
.target(name: "SemanticVersions"),
465471
.target(name: "SHA1"),
472+
.target(name: "Snippets"),
466473
.target(name: "SymbolGraphCompiler"),
467474
.target(name: "SymbolGraphs"),
468475
.target(name: "Symbols"),
@@ -474,6 +481,12 @@ let package:Package = .init(
474481
[
475482
.target(name: "JSON"),
476483
.target(name: "LexicalPaths"),
484+
// This is the point where the symbol graph compiler becomes infected with a
485+
// (non-macro) SwiftSyntax dependency.
486+
//
487+
// This also means that the static symbol graph linker can freely use any
488+
// of the SwiftSyntax-powered plugins, since we have already paid for the
489+
// dependency.
477490
.target(name: "MarkdownPluginSwift"),
478491
.target(name: "Signatures"),
479492
.target(name: "Symbols"),

Sources/CodelinkResolution/CodelinkResolver.Scope.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ extension CodelinkResolver
66
struct Scope
77
{
88
public
9-
let namespace:Symbol.Module
9+
let namespace:Symbol.Module?
1010
public
1111
let imports:[Symbol.Module]
1212
public
1313
let path:[String]
1414

1515
@inlinable public
16-
init(namespace:Symbol.Module, imports:[Symbol.Module] = [], path:[String] = [])
16+
init(namespace:Symbol.Module?, imports:[Symbol.Module] = [], path:[String] = [])
1717
{
1818
self.namespace = namespace
1919
self.imports = imports

Sources/CodelinkResolution/CodelinkResolver.swift

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,22 @@ extension CodelinkResolver
2727
switch link.base
2828
{
2929
case .relative:
30-
for index:Int in
31-
(self.scope.path.startIndex ... self.scope.path.endIndex).reversed()
30+
if let namespace:Symbol.Module = self.scope.namespace
3231
{
33-
let overloads:Overloads = self.table.query(
34-
qualified: ["\(self.scope.namespace)"]
35-
+ self.scope.path[..<index]
36-
+ link.path.components,
37-
suffix: link.suffix)
38-
39-
guard overloads.isEmpty
40-
else
32+
for index:Int in
33+
(self.scope.path.startIndex ... self.scope.path.endIndex).reversed()
4134
{
42-
return overloads
35+
let overloads:Overloads = self.table.query(
36+
qualified: ["\(namespace)"]
37+
+ self.scope.path[..<index]
38+
+ link.path.components,
39+
suffix: link.suffix)
40+
41+
guard overloads.isEmpty
42+
else
43+
{
44+
return overloads
45+
}
4346
}
4447
}
4548
for namespace:Symbol.Module in self.scope.imports where

Sources/DoclinkResolution/DoclinkResolver.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ struct DoclinkResolver
66
public
77
let table:Table
88
public
9-
let scope:Scope
9+
let scope:Scope?
1010

1111
@inlinable public
12-
init(table:Table, scope:Scope)
12+
init(table:Table, scope:Scope?)
1313
{
1414
self.table = table
1515
self.scope = scope
@@ -20,11 +20,12 @@ extension DoclinkResolver
2020
public
2121
func resolve(_ link:Doclink) -> Int32?
2222
{
23-
if !link.absolute
23+
if !link.absolute,
24+
let scope:Scope = self.scope
2425
{
25-
for index:Int in self.scope.indices.reversed()
26+
for index:Int in scope.indices.reversed()
2627
{
27-
let path:DoclinkResolutionPath = .join(self.scope[...index] + link.path)
28+
let path:DoclinkResolutionPath = .join(scope[...index] + link.path)
2829
if let address:Int32 = self.table.entries[path]
2930
{
3031
return address

Sources/MarkdownPluginSwift/MarkdownCodeLanguage.Swift.Highlighter.swift

Lines changed: 0 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import MarkdownABI
2-
import MarkdownRendering
32
import SwiftIDEUtils
43
import SwiftParser
54
import SwiftSyntax
@@ -15,124 +14,6 @@ extension MarkdownCodeLanguage.Swift
1514
}
1615
}
1716
}
18-
19-
extension MarkdownCodeLanguage.Swift.Highlighter
20-
{
21-
public
22-
func _parse(snippet utf8:[UInt8])
23-
{
24-
// It is safe to escape the pointer to ``Parser.parse(source:maximumNestingLevel:)``,
25-
// see: https://swiftinit.org/docs/swift-syntax/swiftparser/parser.init(_:maximumnestinglevel:parsetransition:arena:)
26-
let parsed:SourceFileSyntax = utf8.withUnsafeBufferPointer
27-
{
28-
Parser.parse(source: $0)
29-
}
30-
var start:AbsolutePosition = parsed.position
31-
var text:String = ""
32-
lines:
33-
for piece:TriviaPiece in parsed.leadingTrivia
34-
{
35-
let line:String
36-
let skip:Int
37-
switch piece
38-
{
39-
case .lineComment(let text):
40-
start += piece.sourceLength
41-
line = text
42-
skip = 2
43-
44-
case .docLineComment(let text):
45-
start += piece.sourceLength
46-
line = text
47-
skip = 3
48-
49-
case .newlines(1), .carriageReturnLineFeeds(1):
50-
start += piece.sourceLength
51-
continue
52-
53-
case .newlines, .carriageReturnLineFeeds:
54-
start += piece.sourceLength
55-
break lines
56-
57-
default:
58-
break lines
59-
}
60-
61-
guard
62-
let i:String.Index = line.index(line.startIndex,
63-
offsetBy: skip,
64-
limitedBy: line.endIndex)
65-
else
66-
{
67-
fatalError("Encountered a line comment with no leading slashes!")
68-
}
69-
70-
text += line[i...].drop(while: \.isWhitespace)
71-
text.append("\n")
72-
}
73-
74-
var parser:SnippetParser = .init(start: start)
75-
for token:TokenSyntax in parsed.tokens(viewMode: .sourceAccurate)
76-
{
77-
parser.visit(token: token)
78-
}
79-
80-
let slices:[SnippetParser.Slice] = parser.finish(at: parsed.endPosition, in: utf8)
81-
82-
var spans:SyntaxClassifications.Iterator = parsed.classifications.makeIterator()
83-
var span:SyntaxClassifiedRange? = spans.next()
84-
let rendered:[MarkdownBytecode] = slices.map
85-
{
86-
(slice:SnippetParser.Slice) in .init
87-
{
88-
ranges:
89-
for var range:Range<Int> in slice.ranges
90-
{
91-
while let highlight:SyntaxClassifiedRange = span
92-
{
93-
if range.upperBound < highlight.endOffset
94-
{
95-
// This range is strictly contained within the current highlight.
96-
$0[highlight: highlight.kind] = utf8[range]
97-
continue ranges
98-
}
99-
100-
span = spans.next()
101-
102-
if range.lowerBound >= highlight.endOffset
103-
{
104-
// This range does not overlap with the current highlight at all.
105-
continue
106-
}
107-
108-
if range.upperBound == highlight.endOffset
109-
{
110-
// This range ends at the end of the current highlight.
111-
$0[highlight: highlight.kind] = utf8[range]
112-
continue ranges
113-
}
114-
else
115-
{
116-
// This range ends after the end of the current highlight.
117-
let overlap:Range<Int> = range.lowerBound ..< highlight.endOffset
118-
$0[highlight: highlight.kind] = utf8[overlap]
119-
120-
range = highlight.endOffset ..< range.upperBound
121-
}
122-
}
123-
}
124-
}
125-
}
126-
127-
for (slice, rendered):(SnippetParser.Slice, MarkdownBytecode) in zip(slices, rendered)
128-
{
129-
print("Snippet '\(slice.id)':")
130-
print("--------------------")
131-
print("\(rendered.safe)")
132-
print("--------------------")
133-
}
134-
}
135-
}
13617
extension MarkdownCodeLanguage.Swift.Highlighter:MarkdownCodeHighlighter
13718
{
13819
public

Sources/MarkdownPluginSwift/MarkdownCodeLanguage.Swift.swift

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
import MarkdownABI
2+
import MarkdownRendering
3+
import Snippets
4+
import SwiftIDEUtils
5+
import SwiftParser
6+
import SwiftSyntax
27

38
extension MarkdownCodeLanguage
49
{
@@ -19,3 +24,119 @@ extension MarkdownCodeLanguage.Swift:MarkdownCodeLanguageType
1924
@inlinable public
2025
var highlighter:Highlighter { .init() }
2126
}
27+
extension MarkdownCodeLanguage.Swift
28+
{
29+
public
30+
func parse(snippet utf8:[UInt8]) -> (overview:String, slices:[Snippet.Slice])
31+
{
32+
// It is safe to escape the pointer to ``Parser.parse(source:maximumNestingLevel:)``,
33+
// see: https://swiftinit.org/docs/swift-syntax/swiftparser/parser.init(_:maximumnestinglevel:parsetransition:arena:)
34+
let parsed:SourceFileSyntax = utf8.withUnsafeBufferPointer
35+
{
36+
Parser.parse(source: $0)
37+
}
38+
var start:AbsolutePosition = parsed.position
39+
var text:String = ""
40+
lines:
41+
for piece:TriviaPiece in parsed.leadingTrivia
42+
{
43+
let line:String
44+
let skip:Int
45+
switch piece
46+
{
47+
case .lineComment(let text):
48+
start += piece.sourceLength
49+
line = text
50+
skip = 2
51+
52+
case .docLineComment(let text):
53+
start += piece.sourceLength
54+
line = text
55+
skip = 3
56+
57+
case .newlines(1), .carriageReturnLineFeeds(1):
58+
start += piece.sourceLength
59+
continue
60+
61+
case .newlines, .carriageReturnLineFeeds:
62+
start += piece.sourceLength
63+
break lines
64+
65+
default:
66+
break lines
67+
}
68+
69+
guard
70+
let i:String.Index = line.index(line.startIndex,
71+
offsetBy: skip,
72+
limitedBy: line.endIndex)
73+
else
74+
{
75+
fatalError("Encountered a line comment with no leading slashes!")
76+
}
77+
78+
text += line[i...].drop(while: \.isWhitespace)
79+
text.append("\n")
80+
}
81+
82+
var parser:SnippetParser = .init(start: start)
83+
for token:TokenSyntax in parsed.tokens(viewMode: .sourceAccurate)
84+
{
85+
parser.visit(token: token)
86+
}
87+
88+
let slices:[SnippetParser.Slice] = parser.finish(at: parsed.endPosition, in: utf8)
89+
90+
var spans:SyntaxClassifications.Iterator = parsed.classifications.makeIterator()
91+
var span:SyntaxClassifiedRange? = spans.next()
92+
93+
let rendered:[Snippet.Slice] = slices.map
94+
{
95+
(slice:SnippetParser.Slice) in
96+
97+
let bytecode:MarkdownBytecode = .init
98+
{
99+
ranges:
100+
for var range:Range<Int> in slice.ranges
101+
{
102+
while let highlight:SyntaxClassifiedRange = span
103+
{
104+
if range.upperBound < highlight.endOffset
105+
{
106+
// This range is strictly contained within the current highlight.
107+
$0[highlight: highlight.kind] = utf8[range]
108+
continue ranges
109+
}
110+
111+
span = spans.next()
112+
113+
if range.lowerBound >= highlight.endOffset
114+
{
115+
// This range does not overlap with the current highlight at all.
116+
continue
117+
}
118+
119+
if range.upperBound == highlight.endOffset
120+
{
121+
// This range ends at the end of the current highlight.
122+
$0[highlight: highlight.kind] = utf8[range]
123+
continue ranges
124+
}
125+
else
126+
{
127+
// This range ends after the end of the current highlight.
128+
let overlap:Range<Int> = range.lowerBound ..< highlight.endOffset
129+
$0[highlight: highlight.kind] = utf8[overlap]
130+
131+
range = highlight.endOffset ..< range.upperBound
132+
}
133+
}
134+
}
135+
}
136+
137+
return .init(id: slice.id, bytecode: bytecode)
138+
}
139+
140+
return (text, rendered)
141+
}
142+
}

Sources/MarkdownPluginSwift/Signature.Expanded (ext).swift renamed to Sources/MarkdownPluginSwift/Signatures/Signature.Expanded (ext).swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
2-
31
import MarkdownABI
42
import Signatures
53

0 commit comments

Comments
 (0)