├── .gitignore
├── FormatImpl
├── FormatImpl-Bridging-Header.h
├── FormatImpl.swift
├── Info.plist
├── LICENCE.md
├── clang-format
├── clang_format.sh
├── swift_format.sh
└── swiftformat
├── FormatRelay
├── FormatRelay-Bridging-Header.h
├── FormatRelay.entitlements
└── Info.plist
├── GitBlameImpl
├── GitBlameImpl-Bridging-Header.h
├── Info.plist
└── gitblame.py
├── GitBlameRelay
├── GitBlameRelay-Bridging-Header.h
├── GitBlameRelay.entitlements
└── Info.plist
├── GitDiffImpl
├── DiffMatchPatch
│ ├── DMDiff.h
│ ├── DMDiff.m
│ ├── DMPatch.h
│ ├── DMPatch.m
│ ├── DiffMatchPatch.h
│ ├── DiffMatchPatch.m
│ ├── DiffMatchPatchCFUtilities.h
│ ├── DiffMatchPatchCFUtilities.m
│ ├── DiffMatchPatchInternals.h
│ ├── LICENSE
│ ├── NSString+EscapeHTMLCharacters.h
│ ├── NSString+EscapeHTMLCharacters.m
│ ├── NSString+UriCompatibility.h
│ └── NSString+UriCompatibility.m
├── DiffProcessor.swift
├── GitDiffImpl-Bridging-Header.h
├── GitDiffImpl.entitlements
├── GitDiffImpl.swift
└── Info.plist
├── GitDiffRelay
├── GitDiffRelay-Bridging-Header.h
├── GitDiffRelay.entitlements
└── Info.plist
├── InferImpl
├── InferImpl-Bridging-Header.h
├── InferImpl.swift
├── Info.plist
├── infer.mm
├── infer.sh
└── sourcekitd.h
├── InferRelay
├── InferRelay-Bridging-Header.h
├── InferRelay.entitlements
└── Info.plist
├── LICENSE
├── LNProvider.t2d
├── LNProvider.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── LNProvider
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── icon_128x128.png
│ │ ├── icon_128x128@2x.png
│ │ ├── icon_16x16.png
│ │ ├── icon_16x16.tiff
│ │ ├── icon_16x16@2x.png
│ │ ├── icon_256x256.png
│ │ ├── icon_256x256@2x.png
│ │ ├── icon_32x32.png
│ │ ├── icon_32x32@2x.png
│ │ ├── icon_512x512.png
│ │ └── icon_512x512@2x.png
│ └── Contents.json
├── Base.lproj
│ └── MainMenu.xib
├── DefaultManager.swift
├── Info.plist
├── LNProvider-Bridging-Header.h
└── LNProvider.entitlements
├── LNProviderTests
├── Info.plist
├── LNProviderTests-Bridging-Header.h
├── LNProviderTests.swift
└── example_diff.txt
├── LNProviderUITests
├── Info.plist
└── LNProviderUITests.swift
├── LNXcodeSupport
├── Info.plist
├── KeyPath.swift
├── LNExtensionClient.h
├── LNExtensionClient.m
├── LNExtensionClientDO.h
├── LNExtensionClientDO.m
├── LNExtensionProtocol.h
├── LNFileHighlights.h
├── LNFileHighlights.mm
├── LNHighlightGutter.h
├── LNHighlightGutter.m
├── LNXcodeSupport.h
├── LNXcodeSupport.mm
├── NSColor+NSString.h
├── NSColor+NSString.m
├── XcodePrivate.h
└── undo.png
├── LineNumberPlugin.pages
├── README.md
└── SharedXPC
├── LNExtensionBase.swift
├── LNExtensionRelay.swift
├── LNScriptImpl.swift
├── LineGenerators.swift
└── main.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | build/*
2 | *Library/*
3 | *xcuserdata*
4 |
--------------------------------------------------------------------------------
/FormatImpl/FormatImpl-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "LNExtensionProtocol.h"
6 | #import "LNFileHighlights.h"
7 |
8 | #import "DiffMatchPatch.h"
9 | #import "DMDiff.h"
10 |
--------------------------------------------------------------------------------
/FormatImpl/FormatImpl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FormatImpl.swift
3 | // LNProvider
4 | //
5 | // Created by John Holdsworth on 03/04/2017.
6 | // Copyright © 2017 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | let implementationFactory = FormatImpl.self
12 | var diffgen = DiffProcessor()
13 |
14 | class FormatDefaults: DefaultManager {
15 |
16 | open override var modifiedKey: String { return formatKey }
17 |
18 | }
19 |
20 | open class FormatImpl: LNExtensionBase, LNExtensionService {
21 |
22 | open var defaults: DefaultManager {
23 | return FormatDefaults()
24 | }
25 |
26 | open override func getConfig(_ callback: @escaping LNConfigCallback) {
27 | callback([
28 | LNApplyTitleKey: "Format Lint",
29 | LNApplyPromptKey: "Apply style suggestion to lines %d-%d",
30 | LNApplyConfirmKey: "Modify",
31 | ])
32 | }
33 |
34 | open var scripts = [
35 | "swift": "swift_format",
36 | "m": "clang_format",
37 | "mm": "clang_format",
38 | "h": "clang_format",
39 | "cpp": "clang_format",
40 | "c": "clang_format"
41 | ]
42 |
43 | open func requestHighlights(forFile filepath: String, callback: @escaping LNHighlightCallback) {
44 | let url = URL(fileURLWithPath: filepath)
45 |
46 | guard let diffScript = scripts[url.pathExtension] else {
47 | callback(nil, nil)
48 | return
49 | }
50 |
51 | guard let script = Bundle.main.path(forResource: diffScript, ofType: "sh") else {
52 | callback(nil, error(description: "script \(diffScript).sh not in XPC bundle"))
53 | return
54 | }
55 |
56 | DispatchQueue.global().async {
57 | let generator = TaskGenerator(launchPath: script,
58 | arguments: [url.lastPathComponent],
59 | directory: url.deletingLastPathComponent().path)
60 |
61 | for _ in 0 ..< 2 {
62 | _ = generator.next()
63 | }
64 |
65 | let highlights = diffgen.generateHighlights(sequence: generator.lineSequence, defaults: self.defaults)
66 | callback(highlights.jsonData(), nil)
67 | }
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/FormatImpl/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | FormatImpl
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | XPC!
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | NSHumanReadableCopyright
24 | Copyright © 2017 John Holdsworth. All rights reserved.
25 | XPCService
26 |
27 | ServiceType
28 | Application
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/FormatImpl/LICENCE.md:
--------------------------------------------------------------------------------
1 | SwiftFormat
2 |
3 | Copyright (c) 2016 Nick Lockwood
4 |
5 | This software is provided 'as-is', without any express or implied
6 | warranty. In no event will the authors be held liable for any damages
7 | arising from the use of this software.
8 |
9 | Permission is granted to anyone to use this software for any purpose,
10 | including commercial applications, and to alter it and redistribute it
11 | freely, subject to the following restrictions:
12 |
13 | 1. The origin of this software must not be misrepresented; you must not
14 | claim that you wrote the original software. If you use this software
15 | in a product, an acknowledgment in the product documentation would be
16 | appreciated but is not required.
17 |
18 | 2. Altered source versions must be plainly marked as such, and must not be
19 | misrepresented as being the original software.
20 |
21 | 3. This notice may not be removed or altered from any source distribution.
--------------------------------------------------------------------------------
/FormatImpl/clang-format:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/GitDiff/1089f1f4eadbb7297ffb598983402ac383ae3ddd/FormatImpl/clang-format
--------------------------------------------------------------------------------
/FormatImpl/clang_format.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # clang_format.sh
4 | # LNProvider
5 | #
6 | # Created by John Holdsworth on 04/04/2017.
7 | # Copyright © 2017 John Holdsworth. All rights reserved.
8 |
9 | INDENT=`defaults read LineNumber FormatIndent`
10 | XCODE_STYLE="{ IndentWidth: $INDENT, TabWidth: $INDENT, ObjCBlockIndentWidth: $INDENT, ColumnLimit: 0 }"
11 |
12 | diff -Naur <("$(dirname "$0")/clang-format" -style="$XCODE_STYLE" <"$1") "$1"
13 |
--------------------------------------------------------------------------------
/FormatImpl/swift_format.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # swift_format.sh
4 | # LNProvider
5 | #
6 | # Created by John Holdsworth on 03/04/2017.
7 | # Copyright © 2017 John Holdsworth. All rights reserved.
8 |
9 | # https://github.com/nicklockwood/SwiftFormat/releases/tag/0.28.2
10 |
11 | diff -Naur <("$(dirname "$0")/swiftformat" <"$1") "$1"
12 |
--------------------------------------------------------------------------------
/FormatImpl/swiftformat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/GitDiff/1089f1f4eadbb7297ffb598983402ac383ae3ddd/FormatImpl/swiftformat
--------------------------------------------------------------------------------
/FormatRelay/FormatRelay-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // FormatRelay-Bridging-Header.h
3 | // LNProvider
4 | //
5 | // Created by John Holdsworth on 03/04/2017.
6 | // Copyright © 2017 John Holdsworth. All rights reserved.
7 | //
8 |
9 | #import "LNExtensionProtocol.h"
10 |
11 | #define EXTENSION_IMPL_SERVICE "com.johnholdsworth.FormatImpl"
12 |
--------------------------------------------------------------------------------
/FormatRelay/FormatRelay.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.network.client
8 |
9 | com.apple.security.network.server
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/FormatRelay/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | FormatRelay
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | XPC!
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | NSHumanReadableCopyright
24 | Copyright © 2017 John Holdsworth. All rights reserved.
25 | XPCService
26 |
27 | ServiceType
28 | Application
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/GitBlameImpl/GitBlameImpl-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "LNExtensionProtocol.h"
6 |
7 | #define EXTENSION_IMPL_SCRIPT "gitblame"
8 |
--------------------------------------------------------------------------------
/GitBlameImpl/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | GitBlameImpl
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | XPC!
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | NSHumanReadableCopyright
24 | Copyright © 2017 John Holdsworth. All rights reserved.
25 | XPCService
26 |
27 | ServiceType
28 | Application
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/GitBlameImpl/gitblame.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | # gitblame.py
5 | # LNProvider
6 | #
7 | # Created by John Holdsworth on 31/03/2017.
8 | # Copyright © 2017 John Holdsworth. All rights reserved.
9 |
10 | import subprocess
11 | import json
12 | import time
13 | import math
14 | import sys
15 | import re
16 |
17 | file = sys.argv[1]
18 | parser = re.compile(r"^\^?(\S+) (?:\S+\s+)*\((.+) +(\d+) \S+ +(\d+)\)")
19 |
20 | def readdefault( key, default ):
21 | value = subprocess.Popen(['/usr/bin/env', 'defaults', 'read', 'LineNumber', key],
22 | stdout=subprocess.PIPE).stdout.read()
23 | return value if value != '' else default
24 |
25 | recent = float(readdefault('RecentDays', '7'))*24*60*60
26 | decay = recent
27 |
28 | color = readdefault('RecentColor', "0.5 1.0 0.5 1")
29 | color = re.sub(r' [\d.]+\n?$', ' %f', color)
30 |
31 | lastcommit = None
32 | commits = {}
33 | output = {}
34 |
35 | proc = subprocess.Popen(['/usr/bin/env', 'git', 'blame', '-t', file],stdout=subprocess.PIPE)
36 | for line in iter(proc.stdout.readline,''):
37 | match = parser.match(line)
38 | commit = match.group(1)
39 | if commit.startswith('00000000'):
40 | continue
41 |
42 | who = match.group(2)
43 | when = match.group(3)
44 | lineno = match.group(4)
45 |
46 | if commit != lastcommit:
47 | start = lineno
48 | lastcommit = commit
49 |
50 | age = time.time() - int(when)
51 | seen = commits.get(commit)
52 | if seen:
53 | alias = {'alias': seen['lineno']}
54 | if lineno == start:
55 | alias['start'] = start
56 | seen['lineno'] = start
57 | output[lineno] = alias
58 | elif age < recent:
59 | log = subprocess.Popen(['/usr/bin/env', 'git', 'show', '--pretty=medium', '-s', commit],
60 | stdout=subprocess.PIPE).stdout.read()
61 | commits[commit] = {'lineno': lineno, 'log': log}
62 | output[lineno] = {'text': log, 'start': start, 'color': color % math.exp(-age/decay)}
63 |
64 | print json.dumps(output)
65 |
--------------------------------------------------------------------------------
/GitBlameRelay/GitBlameRelay-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "LNExtensionProtocol.h"
6 |
7 | #define EXTENSION_IMPL_SERVICE "com.johnholdsworth.GitBlameImpl"
8 |
--------------------------------------------------------------------------------
/GitBlameRelay/GitBlameRelay.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.network.client
8 |
9 | com.apple.security.network.server
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/GitBlameRelay/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | GitBlameRelay
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | XPC!
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | NSHumanReadableCopyright
24 | Copyright © 2017 John Holdsworth. All rights reserved.
25 | XPCService
26 |
27 | ServiceType
28 | Application
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/GitDiffImpl/DiffMatchPatch/DMDiff.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Diff Match and Patch
3 | *
4 | * Copyright 2010 geheimwerk.de.
5 | * http://code.google.com/p/google-diff-match-patch/
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * Author: fraser@google.com (Neil Fraser)
20 | * ObjC port: jan@geheimwerk.de (Jan Weiß)
21 | * Refactoring & mangling: @inquisitivesoft (Harry Jordan)
22 | */
23 |
24 |
25 | /*
26 | * The data structure representing a diff is an array of Diff objects:
27 | * [
28 | * Diff(Operation.DIFF_DELETE, "Hello"),
29 | * Diff(Operation.DIFF_INSERT, "Goodbye"),
30 | * Diff(Operation.DIFF_EQUAL, " world.")
31 | * ]
32 | * which means: delete "Hello", add "Goodbye" and keep " world."
33 | */
34 |
35 |
36 | typedef enum {
37 | DIFF_DELETE = 1,
38 | DIFF_INSERT = 2,
39 | DIFF_EQUAL = 3
40 | } DMDiffOperation;
41 |
42 |
43 | #import
44 |
45 | @interface DMDiff :NSObject {
46 | }
47 |
48 | @property (nonatomic, assign) DMDiffOperation operation;
49 | @property (nonatomic, copy) NSString *text;
50 |
51 | + (id)diffWithOperation:(DMDiffOperation)anOperation andText:(NSString *)aText;
52 | - (id)initWithOperation:(DMDiffOperation)anOperation andText:(NSString *)aText;
53 |
54 | @end
55 |
--------------------------------------------------------------------------------
/GitDiffImpl/DiffMatchPatch/DMDiff.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Diff Match and Patch
3 | *
4 | * Copyright 2010 geheimwerk.de.
5 | * http://code.google.com/p/google-diff-match-patch/
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * Author: fraser@google.com (Neil Fraser)
20 | * ObjC port: jan@geheimwerk.de (Jan Weiß)
21 | * Refactoring & mangling: @inquisitivesoft (Harry Jordan)
22 | */
23 |
24 | #pragma clang diagnostic push
25 | #pragma clang diagnostic ignored "-Wdocumentation"
26 |
27 | #import "DMDiff.h"
28 |
29 |
30 | @implementation DMDiff
31 |
32 | /**
33 | * Constructor. Initializes the diff with the provided values.
34 | * @param operation One of DIFF_INSERT, DIFF_DELETE or DIFF_EQUAL.
35 | * @param text The text being applied.
36 | */
37 | + (id)diffWithOperation:(DMDiffOperation)anOperation andText:(NSString *)aText
38 | {
39 | return [[self alloc] initWithOperation:anOperation andText:aText];
40 | }
41 |
42 |
43 | - (id)initWithOperation:(DMDiffOperation)anOperation andText:(NSString *)aText
44 | {
45 | self = [super init];
46 |
47 | if(self) {
48 | self.operation = anOperation;
49 | self.text = aText;
50 | }
51 |
52 | return self;
53 | }
54 |
55 |
56 | - (id)copyWithZone:(NSZone *)zone
57 | {
58 | return [[[self class] allocWithZone:zone] initWithOperation:self.operation andText:self.text];
59 | }
60 |
61 |
62 | /**
63 | * Display a human-readable version of this Diff.
64 | * @return text version.
65 | */
66 | - (NSString *)description
67 | {
68 | NSString *prettyText = [self.text stringByReplacingOccurrencesOfString:@"\n" withString:@"\u00b6"];
69 | NSString *operationName = nil;
70 |
71 | switch(self.operation) {
72 | case DIFF_DELETE:
73 | operationName = @"DELETE";
74 | break;
75 |
76 | case DIFF_INSERT:
77 | operationName = @"INSERT";
78 | break;
79 |
80 | case DIFF_EQUAL:
81 | operationName = @"EQUAL";
82 | break;
83 |
84 | default:
85 | break;
86 | }
87 |
88 | return [NSString stringWithFormat:@"%@ (%@,\"%@\")", [super description], operationName, prettyText];
89 | }
90 |
91 |
92 | /**
93 | * Is this Diff equivalent to another Diff?
94 | * @param obj Another Diff to compare against.
95 | * @return YES or NO.
96 | */
97 | - (BOOL)isEqual:(id)obj
98 | {
99 | // If parameter is nil return NO.
100 | if(obj == nil)
101 | return NO;
102 |
103 | // If parameter cannot be cast to Diff return NO.
104 | if(![obj isKindOfClass:[DMDiff class]])
105 | return NO;
106 |
107 | // Return YES if the fields match.
108 | DMDiff *p = (DMDiff *)obj;
109 | return p.operation == self.operation && [p.text isEqualToString:self.text];
110 | }
111 |
112 | - (BOOL)isEqualToDiff:(DMDiff *)obj
113 | {
114 | // If parameter is nil return NO.
115 | if(obj == nil)
116 | return NO;
117 |
118 | // Return YES if the fields match.
119 | return obj.operation == self.operation && [obj.text isEqualToString:self.text];
120 | }
121 |
122 | - (NSUInteger)hash
123 | {
124 | return [_text hash] ^ (NSUInteger)_operation;
125 | }
126 |
127 | @end
128 |
--------------------------------------------------------------------------------
/GitDiffImpl/DiffMatchPatch/DMPatch.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Diff Match and Patch
3 | *
4 | * Copyright 2010 geheimwerk.de.
5 | * http://code.google.com/p/google-diff-match-patch/
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * Author: fraser@google.com (Neil Fraser)
20 | * ObjC port: jan@geheimwerk.de (Jan Weiß)
21 | * Refactoring & mangling: @inquisitivesoft (Harry Jordan)
22 | */
23 |
24 |
25 | #import
26 |
27 |
28 | @interface DMPatch : NSObject {
29 | }
30 |
31 | @property (nonatomic, strong) NSMutableArray *diffs;
32 | @property (nonatomic, assign) NSUInteger start1;
33 | @property (nonatomic, assign) NSUInteger start2;
34 | @property (nonatomic, assign) NSUInteger length1;
35 | @property (nonatomic, assign) NSUInteger length2;
36 |
37 | - (void)addContext:(NSString *)text withMargin:(NSInteger)patchMargin maximumBits:(NSUInteger)maximumBits;
38 | - (NSString *)patchText;
39 |
40 | @end
--------------------------------------------------------------------------------
/GitDiffImpl/DiffMatchPatch/DMPatch.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Diff Match and Patch
3 | *
4 | * Copyright 2010 geheimwerk.de.
5 | * http://code.google.com/p/google-diff-match-patch/
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * Author: fraser@google.com (Neil Fraser)
20 | * ObjC port: jan@geheimwerk.de (Jan Weiß)
21 | * Refactoring & mangling: @inquisitivesoft (Harry Jordan)
22 | */
23 |
24 | #pragma clang diagnostic push
25 | #pragma clang diagnostic ignored "-Wdocumentation"
26 |
27 | #import "DMPatch.h"
28 | #import "DMDiff.h"
29 |
30 | #import "DiffMatchPatchInternals.h" // DMPatch uses the MAX_OF_CONST_AND_DIFF macro
31 | #import "DiffMatchPatchCFUtilities.h"
32 | #import "NSString+UriCompatibility.h"
33 |
34 |
35 | @implementation DMPatch
36 |
37 |
38 | - (id)init
39 | {
40 | self = [super init];
41 |
42 | if(self) {
43 | self.diffs = [NSMutableArray array];
44 | }
45 |
46 | return self;
47 | }
48 |
49 |
50 | - (id)copyWithZone:(NSZone *)zone
51 | {
52 | DMPatch *newPatch = [[[self class] allocWithZone:zone] init];
53 |
54 | newPatch.diffs = [[NSMutableArray alloc] initWithArray:self.diffs copyItems:YES];
55 | newPatch.start1 = self.start1;
56 | newPatch.start2 = self.start2;
57 | newPatch.length1 = self.length1;
58 | newPatch.length2 = self.length2;
59 |
60 | return newPatch;
61 | }
62 |
63 |
64 | - (NSString *)description
65 | {
66 | return [[super description] stringByAppendingFormat:@" %@", [self patchText]];
67 | }
68 |
69 |
70 | /**
71 | * Emulate GNU diff's format.
72 | * Header: @@ -382,8 +481,9 @@
73 | * Indicies are printed as 1-based, not 0-based.
74 | * @return The GNU diff NSString.
75 | */
76 | - (NSString *)patchText
77 | {
78 | NSString *coords1;
79 | NSString *coords2;
80 |
81 | if(self.length1 == 0) {
82 | coords1 = [NSString stringWithFormat:@"%lu,0",
83 | (unsigned long)self.start1];
84 | } else if(self.length1 == 1) {
85 | coords1 = [NSString stringWithFormat:@"%lu",
86 | (unsigned long)self.start1 + 1];
87 | } else {
88 | coords1 = [NSString stringWithFormat:@"%lu,%lu",
89 | (unsigned long)self.start1 + 1, (unsigned long)self.length1];
90 | }
91 |
92 | if(self.length2 == 0) {
93 | coords2 = [NSString stringWithFormat:@"%lu,0",
94 | (unsigned long)self.start2];
95 | } else if(self.length2 == 1) {
96 | coords2 = [NSString stringWithFormat:@"%lu",
97 | (unsigned long)self.start2 + 1];
98 | } else {
99 | coords2 = [NSString stringWithFormat:@"%lu,%lu",
100 | (unsigned long)self.start2 + 1, (unsigned long)self.length2];
101 | }
102 |
103 | NSMutableString *text = [NSMutableString stringWithFormat:@"@@ -%@ +%@ @@\n",
104 | coords1, coords2];
105 |
106 | // Escape the body of the patch with %xx notation.
107 | for(DMDiff *aDiff in self.diffs) {
108 | switch(aDiff.operation) {
109 | case DIFF_INSERT:
110 | [text appendString:@"+"];
111 | break;
112 |
113 | case DIFF_DELETE:
114 | [text appendString:@"-"];
115 | break;
116 |
117 | case DIFF_EQUAL:
118 | [text appendString:@" "];
119 | break;
120 | }
121 |
122 | [text appendString:[aDiff.text encodedURIString]];
123 | [text appendString:@"\n"];
124 | }
125 |
126 | return text;
127 | }
128 |
129 |
130 |
131 | /**
132 | * Increase the context until it is unique,
133 | * but don't let the pattern expand beyond DIFF_MATCH_MAX_BITS.
134 | * @param patch The patch to grow.
135 | * @param text Source text.
136 | */
137 |
138 | - (void)addContext:(NSString *)text withMargin:(NSInteger)patchMargin maximumBits:(NSUInteger)maximumBits
139 | {
140 | if(text.length == 0)
141 | return;
142 |
143 | NSString *pattern = [text substringWithRange:NSMakeRange(self.start2, self.length1)];
144 | NSUInteger padding = 0;
145 |
146 | // Look for the first and last matches of pattern in text. If two
147 | // different matches are found, increase the pattern length.
148 | while([text rangeOfString:pattern options:NSLiteralSearch].location
149 | != [text rangeOfString:pattern options:(NSLiteralSearch | NSBackwardsSearch)].location
150 | && pattern.length < (maximumBits - patchMargin - patchMargin)) {
151 | padding += patchMargin;
152 |
153 | NSRange patternRange = NSMakeRange(MAX_OF_CONST_AND_DIFF(0, self.start2, padding), MIN(text.length, self.start2 + self.length1 + padding));
154 | patternRange.length -= patternRange.location;
155 | pattern = [text substringWithRange:patternRange];
156 | }
157 |
158 | // Add one chunk for good luck.
159 | padding += patchMargin;
160 |
161 | // Add the prefix.
162 | NSRange prefixRange = NSMakeRange(MAX_OF_CONST_AND_DIFF(0, self.start2, padding), 0);
163 | prefixRange.length = self.start2 - prefixRange.location;
164 | NSString *prefix = [text substringWithRange:prefixRange];
165 |
166 | if(prefix.length != 0) {
167 | [self.diffs insertObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:prefix] atIndex:0];
168 | }
169 |
170 |
171 | // Add the suffix.
172 | NSRange suffixRange = NSMakeRange((self.start2 + self.length1), MIN(text.length - self.start2 - self.length1, padding));
173 | NSString *suffix = [text substringWithRange:suffixRange];
174 |
175 | if(suffix.length != 0) {
176 | [self.diffs addObject:[DMDiff diffWithOperation:DIFF_EQUAL andText:suffix]];
177 | }
178 |
179 | // Roll back the start points.
180 | self.start1 -= prefix.length;
181 | self.start2 -= prefix.length;
182 | // Extend the lengths.
183 | self.length1 += prefix.length + suffix.length;
184 | self.length2 += prefix.length + suffix.length;
185 | }
186 |
187 |
188 | @end
189 |
--------------------------------------------------------------------------------
/GitDiffImpl/DiffMatchPatch/DiffMatchPatch.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Diff Match and Patch
3 | *
4 | * Copyright 2010 geheimwerk.de.
5 | * http://code.google.com/p/google-diff-match-patch/
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * Author: fraser@google.com (Neil Fraser)
20 | * ObjC port: jan@geheimwerk.de (Jan Weiß)
21 | * Refactoring & mangling: @inquisitivesoft (Harry Jordan)
22 | *
23 | * If you need more fine grained options
24 | * have a look at DiffMatchPatchInternals.h
25 | *
26 | */
27 |
28 | #pragma clang diagnostic push
29 | #pragma clang diagnostic ignored "-Wdocumentation"
30 |
31 | #import
32 |
33 | #pragma mark -
34 | #pragma mark Generating Diffs
35 |
36 | /**
37 | * Find the differences between two texts.
38 | *
39 | * @param text1 Old NSString to be diffed.
40 | * @param text2 New NSString to be diffed.
41 | * @return Returns an NSArray of DMDiff objects.
42 | */
43 |
44 | NSArray *diff_diffsBetweenTexts(NSString *text1, NSString *text2);
45 |
46 |
47 | /**
48 | * Find the differences between two texts, with a few options
49 | *
50 | * @param text1 Old NSString to be diffed.
51 | * @param text2 New NSString to be diffed.
52 | * @param highQuality Set to FALSE for a faster but less optimal diff
53 | * Setting this to high quality will be around 2 to 3 times slower
54 | *
55 | * @param timeLimit The number in seconds (from the current time) to allow the function to process the diff.
56 | * Enter 0.0 to allow the function an unlimited period.
57 | *
58 | * @return Returns an array of DMDiff objects.
59 | */
60 |
61 | NSArray *diff_diffsBetweenTextsWithOptions(NSString *text1, NSString *text2, BOOL highQuality, NSTimeInterval timeLimit);
62 |
63 |
64 | #pragma mark -
65 | #pragma mark Formatting Diffs into a human readable output
66 |
67 |
68 | /**
69 | * Calculate the first text from an array of DMDiff objects
70 | *
71 | * @param diffs The array of DMDiff objects.
72 | * @return Returns the first text
73 | */
74 |
75 | NSString *diff_text1(NSArray *diffs);
76 |
77 |
78 | /**
79 | * Calculate the second text from an array of DMDiff objects
80 | *
81 | * @param diffs The array of DMDiff objects.
82 | * @return Returns the first text
83 | */
84 |
85 | NSString *diff_text2(NSArray *diffs);
86 |
87 |
88 | /**
89 | * Create a HTML output from an array of DMDiff objects
90 | *
91 | * @param diffs The array of DMDiff objects.
92 | * @return A HTML string
93 | */
94 |
95 | NSString *diff_prettyHTMLFromDiffs(NSArray *diffs);
96 |
97 |
98 | /**
99 | * Create a delta string from an array of DMDiff objects
100 | *
101 | * @param diffs The array of DMDiff objects.
102 | * @return A delta string
103 | */
104 |
105 | NSString *diff_deltaFromDiffs(NSArray *diffs);
106 |
107 |
108 | /**
109 | * Given the original text1, and an encoded NSString which describes the
110 | * operations required to transform text1 into text2, compute the full diff.
111 | *
112 | * @param text1 Source NSString for the diff.
113 | * @param delta Delta text.
114 | * @param error NSError if invalid input.
115 | * @return NSArray of DMDiff objects or nil if invalid.
116 | */
117 |
118 | NSArray *diff_diffsFromOriginalTextAndDelta(NSString *text1, NSString *delta, NSError **error);
119 |
120 |
121 | /**
122 | * Calculate the levenshtein distance for an array of DMDiff objects
123 | * See http://en.wikipedia.org/wiki/Levenshtein_distance#Definition for more info
124 | *
125 | * @param diffs The array of DMDiff objects.
126 | * @return The levenshtein score for the diffs.
127 | */
128 |
129 | NSUInteger diff_levenshtein(NSArray *diffs);
130 |
131 |
132 | #pragma mark -
133 | #pragma mark Searching text using fuzzy matching
134 |
135 | /**
136 | * Locate the best instance of 'pattern' in 'text' near 'nearestLocation'.
137 | * Returns NSNotFound if no match found.
138 | *
139 | * @param text The text to search.
140 | * @param pattern The pattern to search for.
141 | * @param approximateLocation The location to search around.
142 | * @return Index of the best match or NSNotFound.
143 | */
144 |
145 | NSUInteger match_locationOfMatchInText(NSString *text, NSString *pattern, NSUInteger approximateLocation);
146 |
147 |
148 | /**
149 | * Locate the best instance of 'pattern' in 'text' near 'nearestLocation'.
150 | * Returns NSNotFound if no match found.
151 | *
152 | * @param text The text to search.
153 | * @param pattern The pattern to search for.
154 | * @param approximateLocation The location to search around.
155 | * @param matchThreshold How closely the minimum matching text should match the search pattern. The default is 0.5f
156 | * @param matchDistance How far away from the approximateLocation to search. The default is 1000 characters
157 | * @return Index of the best match or NSNotFound.
158 | */
159 |
160 | NSUInteger match_locationOfMatchInTextWithOptions(NSString *text, NSString *pattern, NSUInteger approximateLocation, CGFloat matchThreshold, NSUInteger matchDistance);
161 |
162 |
163 | #pragma mark -
164 | #pragma mark Patching text
165 |
166 | /**
167 | * Generate an array of DMPatches from two texts
168 | *
169 | * @param text1 The first text
170 | * @param text2 The second text
171 | * @return An array of DMPatches
172 | */
173 |
174 | NSArray *patch_patchesFromTexts(NSString *text1, NSString *text2);
175 |
176 |
177 | /**
178 | * Take a list of patches and return a textual representation.
179 | *
180 | * @param patches NSMutableArray of Patch objects.
181 | * @return Text representation of patches.
182 | */
183 |
184 | NSString *patch_patchesToText(NSArray *patches);
185 |
186 |
187 | /**
188 | * Parse a textual representation of patches and return a NSMutableArray of DMPatch objects.
189 | *
190 | * @param textline Text representation of patches.
191 | * @param error NSError if invalid input.
192 | * @return NSArray of Patch objects.
193 | */
194 |
195 | NSArray *patch_parsePatchesFromText(NSString *text, NSError **error);
196 |
197 |
198 | /**
199 | * Merge a set of patches onto the text. Return a patched text, as well
200 | * as an index set of for each value for which patches were applied.
201 | *
202 | * @param patches An NSArray of DMPatch objects
203 | * @param text The old text
204 | * @param indexesOfAppliedPatches An NSIndexSet of the patches, passed by reference (optional)
205 | * Pass NULL if not required
206 | * @return The patched text
207 | */
208 |
209 | NSString *patch_applyPatchesToText(NSArray *sourcePatches, NSString *text, NSIndexSet **indexesOfAppliedPatches);
210 |
211 |
212 |
--------------------------------------------------------------------------------
/GitDiffImpl/DiffMatchPatch/DiffMatchPatchCFUtilities.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Diff Match and Patch
3 | *
4 | * Copyright 2010 geheimwerk.de.
5 | * http://code.google.com/p/google-diff-match-patch/
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * Author: fraser@google.com (Neil Fraser)
20 | * ObjC port: jan@geheimwerk.de (Jan Weiß)
21 | * Refactoring & mangling: @inquisitivesoft (Harry Jordan)
22 | */
23 |
24 |
25 |
26 | CF_INLINE CFStringRef diff_CFStringCreateSubstring(CFStringRef text, CFIndex start_index, CFIndex length)
27 | {
28 | CFStringRef substring = CFStringCreateWithSubstring(kCFAllocatorDefault, text, CFRangeMake(start_index, length));
29 | return substring;
30 | }
31 |
32 | CF_INLINE CFStringRef diff_CFStringCreateRightSubstring(CFStringRef text, CFIndex text_length, CFIndex new_length)
33 | {
34 | return diff_CFStringCreateSubstring(text, text_length - new_length, new_length);
35 | }
36 |
37 | CF_INLINE CFStringRef diff_CFStringCreateLeftSubstring(CFStringRef text, CFIndex new_length)
38 | {
39 | return diff_CFStringCreateSubstring(text, 0, new_length);
40 | }
41 |
42 | CF_INLINE CFStringRef diff_CFStringCreateSubstringWithStartIndex(CFStringRef text, CFIndex start_index)
43 | {
44 | return diff_CFStringCreateSubstring(text, start_index, (CFStringGetLength(text) - start_index) );
45 | }
46 |
47 |
48 | CF_INLINE void diff_CFStringPrepareUniCharBuffer(CFStringRef string, const UniChar **string_chars, UniChar **string_buffer, CFRange string_range)
49 | {
50 | *string_chars = CFStringGetCharactersPtr(string);
51 |
52 | if(*string_chars == NULL) {
53 | // Fallback in case CFStringGetCharactersPtr() didn’t work.
54 | *string_buffer = malloc(string_range.length * sizeof(UniChar) );
55 | CFStringGetCharacters(string, string_range, *string_buffer);
56 | *string_chars = *string_buffer;
57 | }
58 | }
59 |
60 | CF_INLINE CFIndex diff_CFArrayLastValueAsCFIndex(CFMutableArrayRef theArray)
61 | {
62 | return (CFIndex)CFArrayGetValueAtIndex(theArray, CFArrayGetCount(theArray) - 1);
63 | }
64 |
65 | CF_INLINE void diff_CFArrayRemoveLastValue(CFMutableArrayRef theArray)
66 | {
67 | CFArrayRemoveValueAtIndex(theArray, CFArrayGetCount(theArray) - 1);
68 | }
69 |
70 |
71 | CFIndex diff_commonPrefix(CFStringRef text1, CFStringRef text2);
72 | CFIndex diff_commonSuffix(CFStringRef text1, CFStringRef text2);
73 | CFIndex diff_commonOverlap(CFStringRef text1, CFStringRef text2);
74 |
75 | CFArrayRef diff_halfMatchCreate(CFStringRef text1, CFStringRef text2);
76 | CFArrayRef diff_halfMatchICreate(CFStringRef longtext, CFStringRef shorttext, CFIndex i);
77 | CFStringRef diff_linesToCharsMungeCFStringCreate(CFStringRef text, CFMutableArrayRef lineArray, CFMutableDictionaryRef lineHash);
78 | CFStringRef diff_tokensToCharsMungeCFStringCreate(CFStringRef text, CFMutableArrayRef tokenArray, CFMutableDictionaryRef tokenHash, CFOptionFlags tokenizerOptions);
79 | CFStringRef diff_wordsToCharsMungeCFStringCreate(CFStringRef text, CFMutableArrayRef tokenArray, CFMutableDictionaryRef tokenHash);
80 | CFStringRef diff_sentencesToCharsMungeCFStringCreate(CFStringRef text, CFMutableArrayRef tokenArray, CFMutableDictionaryRef tokenHash);
81 | CFStringRef diff_paragraphsToCharsMungeCFStringCreate(CFStringRef text, CFMutableArrayRef tokenArray, CFMutableDictionaryRef tokenHash);
82 | CFStringRef diff_lineBreakDelimiteredToCharsMungeCFStringCreate(CFStringRef text, CFMutableArrayRef tokenArray, CFMutableDictionaryRef tokenHash);
83 | CFStringRef diff_rangesToCharsMungeCFStringCreate(CFStringRef text, CFMutableArrayRef substringArray, CFMutableDictionaryRef substringHash, CFRange *ranges, size_t ranges_count);
84 | CFStringRef diff_charsToTokenCFStringCreate(CFStringRef charsString, CFArrayRef tokenArray);
85 | CFIndex diff_cleanupSemanticScore(CFStringRef one, CFStringRef two);
--------------------------------------------------------------------------------
/GitDiffImpl/DiffMatchPatch/DiffMatchPatchInternals.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Diff Match and Patch
3 | *
4 | * Copyright 2010 geheimwerk.de.
5 | * http://code.google.com/p/google-diff-match-patch/
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * Author: fraser@google.com (Neil Fraser)
20 | * ObjC port: jan@geheimwerk.de (Jan Weiß)
21 | * Refactoring & mangling: @inquisitivesoft (Harry Jordan)
22 | *
23 | *
24 | * This file contains declares the functions that DiffMatchPatch
25 | * uses internally. You might want to use these for more fine grained
26 | * control and particularly for testing.
27 | *
28 | * As a convention, functions which take an object by reference
29 | * e.g. diff_cleanupSemantic(NSMutableArray **diffs) will change the
30 | * objects content in some way.
31 | *
32 | */
33 |
34 |
35 | // Structs which are used internally to define properties
36 |
37 | struct DiffProperties {
38 | BOOL checkLines; // Set to YES for a faster but less optimal diff
39 | NSTimeInterval deadline;
40 | };
41 |
42 | typedef struct DiffProperties DiffProperties;
43 |
44 |
45 | struct MatchProperties {
46 | CGFloat matchThreshold;
47 | NSUInteger matchDistance;
48 | NSUInteger matchMaximumBits; // The number of bits in an int
49 | };
50 |
51 | typedef struct MatchProperties MatchProperties;
52 |
53 |
54 | struct PatchProperties {
55 | DiffProperties diffProperties;
56 | MatchProperties matchProperties;
57 |
58 | CGFloat patchMargin;
59 | CGFloat patchDeleteThreshold;
60 | NSUInteger diffEditingCost;
61 | };
62 |
63 | typedef struct PatchProperties PatchProperties;
64 |
65 |
66 | typedef enum {
67 | DiffWordTokens = 1,
68 | DiffParagraphTokens = 2,
69 | DiffSentenceTokens = 3,
70 | DiffLineBreakDelimiteredTokens = 4
71 | } DiffTokenMode;
72 |
73 |
74 | // Define default properties
75 | //DiffProperties diff_defaultDiffProperties();
76 | //MatchProperties match_defaultMatchProperties();
77 | //PatchProperties patch_defaultPatchProperties();
78 |
79 |
80 | // Internal functions for diffing
81 | NSMutableArray *diff_diffsBetweenTextsWithProperties(NSString *oldText, NSString *newText, DiffProperties properties);
82 | NSUInteger diff_translateLocationFromText1ToText2(NSArray *diffs, NSUInteger location);
83 | NSMutableArray *diff_computeDiffsBetweenTexts(NSString *text1, NSString *text2, DiffProperties properties);
84 | NSMutableArray *diff_computeDiffsUsingLineMode(NSString *text1, NSString *text2, DiffProperties properties);
85 |
86 | NSArray *diff_linesToCharsForStrings(NSString *text1, NSString * text2);
87 | NSArray *diff_tokensToCharsForStrings(NSString *text1, NSString *text2, DiffTokenMode mode);
88 | void diff_charsToLines(NSArray **diffs, NSArray *lineArray);
89 | void diff_charsToTokens(NSArray **diffs, NSArray *tokenArray);
90 |
91 | NSMutableArray *diff_bisectOfStrings(NSString *text1, NSString *text2, DiffProperties properties);
92 | NSMutableArray *diff_bisectSplitOfStrings(NSString *text1, NSString *text2, NSUInteger x, NSUInteger y, DiffProperties properties);
93 |
94 | void diff_cleanupSemantic(NSMutableArray **diffs);
95 | void diff_cleanupMerge(NSMutableArray **diffs);
96 | void diff_cleanupSemanticLossless(NSMutableArray **diffs);
97 | NSInteger diff_cleanupSemanticScoreOfStrings(NSString *text1, NSString *text2);
98 |
99 |
100 | // Internal functions for matching
101 | NSUInteger match_locationOfMatchInTextWithProperties(NSString *text, NSString *pattern, NSUInteger nearLocation, MatchProperties properties);
102 | NSUInteger match_bitapOfTextAndPattern(NSString *text, NSString *pattern, NSUInteger nearLocation, MatchProperties properties);
103 | NSMutableDictionary *match_alphabetFromPattern(NSString *pattern);
104 | double match_bitapScoreForErrorCount(NSUInteger e, NSUInteger x, NSUInteger nearLocation, NSString *pattern, MatchProperties properties);
105 |
106 |
107 | // Internal functions for patching
108 | NSArray *patch_patchesFromTextsWithProperties(NSString *text1, NSString *text2, PatchProperties properties);
109 | NSArray *patch_patchesFromDiffs(NSArray *diffs, PatchProperties properties);
110 | NSArray *patch_patchesFromTextAndDiffs(NSString *text1, NSArray *diffs, PatchProperties properties);
111 | NSString *patch_applyPatchesToTextWithProperties(NSArray *sourcePatches, NSString *text, NSIndexSet **indexesOfAppliedPatches, PatchProperties properties);
112 |
113 | NSString *patch_addPaddingToPatches(NSMutableArray **patches, PatchProperties properties);
114 | void patch_splitMax(NSMutableArray **patches, PatchProperties properties);
115 | void patch_cleanupDiffsForEfficiency(NSMutableArray **diffs, PatchProperties properties);
116 |
117 |
118 | // A convenience function to splice two arrays, likely of DMDiffs or DMPatches
119 | void diff_spliceTwoArrays(NSMutableArray **input, NSUInteger start, NSUInteger count, NSArray *objects);
120 |
121 |
122 | // MAX_OF_CONST_AND_DIFF determines the maximum of two expressions:
123 | // The first is a constant (first parameter) while the second expression is
124 | // the difference between the second and third parameter. The way this is
125 | // calculated prevents integer overflow in the result of the difference.
126 |
127 | #if !defined(MAX_OF_CONST_AND_DIFF)
128 | #define MAX_OF_CONST_AND_DIFF(A, B, C) ((B) <= (C) ? (A) : (B)-(C) + (A))
129 | #endif
130 |
--------------------------------------------------------------------------------
/GitDiffImpl/DiffMatchPatch/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
--------------------------------------------------------------------------------
/GitDiffImpl/DiffMatchPatch/NSString+EscapeHTMLCharacters.h:
--------------------------------------------------------------------------------
1 | /*
2 | * NSString+EscapeHTMLCharacters
3 | * Copyright 2010 Harry Jordan.
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | *
17 | * Authored by @inquisitiveSoft (Harry Jordan)
18 | *
19 | * Heavily inspired by http://google-toolbox-for-mac.googlecode.com/svn/trunk/Foundation/GTMNSString+HTML.m
20 | * in fact the mapOfHTMLEquivalentsForCharacters table is a directly copy
21 | */
22 |
23 |
24 | @interface NSString (DMEscapeHTMLCharacters)
25 |
26 | - (NSString *)stringByEscapingHTML;
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/GitDiffImpl/DiffMatchPatch/NSString+EscapeHTMLCharacters.m:
--------------------------------------------------------------------------------
1 | /*
2 | * NSString+EscapeHTMLCharacters
3 | * Copyright 2010 Harry Jordan.
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | *
17 | * Authored by @inquisitiveSoft (Harry Jordan)
18 | *
19 | * Inspired by http://google-toolbox-for-mac.googlecode.com/svn/trunk/Foundation/GTMNSString+HTML.m
20 | * in fact the mapOfHTMLEquivalentsForCharacters table is a directly copy
21 | */
22 |
23 |
24 | #import "NSString+EscapeHTMLCharacters.h"
25 |
26 |
27 | typedef struct {
28 | char *name;
29 | unichar character;
30 | } DMCharacterDefinition;
31 |
32 |
33 | static DMCharacterDefinition mapOfHTMLEquivalentsForCharacters[] = {
34 | { " ", 9 }, // Tab character
35 |
36 | // Originally from http://www.w3.org/TR/xhtml1/dtds.html#a_dtd_Special_characters
37 | { """, 34 },
38 | { "&", 38 },
39 | { "'", 39 },
40 | { "<", 60 },
41 | { ">", 62 },
42 | { "Œ", 338 },
43 | { "œ", 339 },
44 | { "Š", 352 },
45 | { "š", 353 },
46 | { "Ÿ", 376 },
47 | { "ˆ", 710 },
48 | { "˜", 732 },
49 | { " ", 8194 },
50 | { " ", 8195 },
51 | { " ", 8201 },
52 | { "", 8204 },
53 | { "", 8205 },
54 | { "", 8206 },
55 | { "", 8207 },
56 | { "–", 8211 },
57 | { "—", 8212 },
58 | { "‘", 8216 },
59 | { "’", 8217 },
60 | { "‚", 8218 },
61 | { "“", 8220 },
62 | { "”", 8221 },
63 | { "„", 8222 },
64 | { "†", 8224 },
65 | { "‡", 8225 },
66 | { "‰", 8240 },
67 | { "‹", 8249 },
68 | { "›", 8250 },
69 | { "€", 8364 },
70 | };
71 |
72 | static const size_t numberOfHTMLEquivalents = 34; // ToDo: expand the range of characters
73 |
74 |
75 | int compareCharacterDefinitions(void const *firstEquivalent, void const *secondEquivalent) {
76 | const DMCharacterDefinition firstCharacter = *(const DMCharacterDefinition *)firstEquivalent;
77 | const DMCharacterDefinition secondCharacter = *(const DMCharacterDefinition *)secondEquivalent;
78 |
79 | if(firstCharacter.character < secondCharacter.character)
80 | return -1;
81 | else if(firstCharacter.character == secondCharacter.character)
82 | return 0;
83 |
84 | return 1;
85 | }
86 |
87 |
88 |
89 | @implementation NSString (DMEscapeHTMLCharacters)
90 |
91 |
92 | - (NSString *)stringByEscapingHTML
93 | {
94 | NSInteger length = self.length;
95 | if(length <= 0)
96 | return self;
97 |
98 | __block NSMutableString *result = [[NSMutableString alloc] init];
99 |
100 | // Iteration state
101 | __block BOOL previousCharacterIsWhiteSpace = FALSE;
102 | __block BOOL previousCharacterIsEscapedWhiteSpace = FALSE;
103 |
104 | [self enumerateSubstringsInRange:NSMakeRange(0, self.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
105 | // First, handle spaces as a special case
106 | if([substring isEqualToString:@" "]) {
107 | // If there are more than one space characters in a row then add 's
108 | if(previousCharacterIsWhiteSpace) {
109 | if(!previousCharacterIsEscapedWhiteSpace) {
110 | // Replace the previous normal space character
111 | [result replaceCharactersInRange:NSMakeRange([result length] - 1, 1) withString:@" "];
112 | }
113 |
114 | [result appendString:@" "];
115 | previousCharacterIsEscapedWhiteSpace = TRUE;
116 | } else
117 | [result appendString:@" "];
118 |
119 | previousCharacterIsWhiteSpace = TRUE;
120 | } else {
121 | if(substringRange.length == 1) {
122 | // If the substring can be represented as a single unicode code point
123 | unichar currentCharacter = [substring characterAtIndex:0];
124 |
125 | if([[NSCharacterSet newlineCharacterSet] characterIsMember:currentCharacter]) {
126 | // If the character represents a new line then add a
tag
127 | // Doesn't do any clever parsing of paragraphs
128 | [result appendString:@"
\n"];
129 | } else {
130 | // If character is not a whitespace or newline character then search
131 | // mapOfHTMLEquivalentsForCharacters to see if we can find a replacement for it
132 | DMCharacterDefinition currentCharacterDefinition;
133 | currentCharacterDefinition.character = currentCharacter;
134 | DMCharacterDefinition *searchResult = bsearch(¤tCharacterDefinition, &mapOfHTMLEquivalentsForCharacters, numberOfHTMLEquivalents, sizeof(DMCharacterDefinition), compareCharacterDefinitions);
135 |
136 | if(searchResult != NULL) {
137 | // Append the resulting encoded HTML character
138 | [result appendFormat:@"%s", searchResult->name];
139 | } else {
140 | // Otherwise just append the character
141 | [result appendString:substring];
142 | }
143 | }
144 | } else if(substringRange.length > 1) {
145 | // Otherwise just append the complex character sequence
146 | [result appendString:substring];
147 | }
148 |
149 | previousCharacterIsWhiteSpace = FALSE;
150 | previousCharacterIsEscapedWhiteSpace = FALSE;
151 | }
152 | }];
153 |
154 | return result;
155 | }
156 |
157 |
158 | @end
159 |
--------------------------------------------------------------------------------
/GitDiffImpl/DiffMatchPatch/NSString+UriCompatibility.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Diff Match and Patch
3 | *
4 | * Copyright 2010 geheimwerk.de.
5 | * http://code.google.com/p/google-diff-match-patch/
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * Author: fraser@google.com (Neil Fraser)
20 | * ObjC port: jan@geheimwerk.de (Jan Weiß)
21 | */
22 |
23 |
24 | #import
25 |
26 |
27 | @interface NSString (UriCompatibility)
28 |
29 | - (NSString *)encodedURIString;
30 | - (NSString *)decodedURIString;
31 |
32 | @end
--------------------------------------------------------------------------------
/GitDiffImpl/DiffMatchPatch/NSString+UriCompatibility.m:
--------------------------------------------------------------------------------
1 | /*
2 | * Diff Match and Patch
3 | *
4 | * Copyright 2010 geheimwerk.de.
5 | * http://code.google.com/p/google-diff-match-patch/
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | *
19 | * Author: fraser@google.com (Neil Fraser)
20 | * ObjC port: jan@geheimwerk.de (Jan Weiß)
21 | */
22 |
23 | #pragma clang diagnostic push
24 | #pragma clang diagnostic ignored "-Wdocumentation"
25 | #pragma clang diagnostic ignored "-Wdeprecated"
26 |
27 | #import "NSString+UriCompatibility.h"
28 |
29 |
30 | @implementation NSString (UriCompatibility)
31 |
32 | /**
33 | * Escape excluding selected chars for compatability with JavaScript's encodeURI.
34 | * This method produces uppercase hex.
35 | *
36 | * @param str The CFStringRef to escape.
37 | * @return The escaped CFStringRef.
38 | */
39 | - (NSString *)encodedURIString
40 | {
41 | return (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)self, CFSTR(" !~*'();/?:@&=+$,#"), NULL, kCFStringEncodingUTF8);
42 | }
43 |
44 | /**
45 | * Unescape all percent escapes.
46 | *
47 | * Example: "%3f" -> "?", "%24" -> "$", etc.
48 | *
49 | * @return The unescaped NSString.
50 | */
51 | - (NSString *)decodedURIString
52 | {
53 | return (__bridge_transfer NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(NULL, (CFStringRef)self, CFSTR(""), kCFStringEncodingUTF8);
54 | }
55 |
56 | @end
57 |
--------------------------------------------------------------------------------
/GitDiffImpl/DiffProcessor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DiffProessor.swift
3 | // LNProvider
4 | //
5 | // Created by John Holdsworth on 03/04/2017.
6 | // Copyright © 2017 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class DiffProcessor {
12 |
13 | let regex = try! NSRegularExpression(pattern: "^(?:(?:@@ -\\d+,\\d+ \\+(\\d+),\\d+ @@)|([-+])(.*))", options: [])
14 |
15 | enum Delta {
16 | case start(lineno: Int)
17 | case delete(text: String)
18 | case insert(text: String)
19 | case other
20 | }
21 |
22 | func delta(line: String) -> Delta {
23 | if let match = regex.firstMatch(in: line, options: [], range: NSMakeRange(0, line.utf16.count)) {
24 | if let lineno = match.group(1, in: line) {
25 | return .start(lineno: Int(lineno) ?? -1)
26 | } else if let delta = match.group(2, in: line), let text = match.group(3, in: line) {
27 | if delta == "-" {
28 | return .delete(text: text)
29 | } else {
30 | return .insert(text: text)
31 | }
32 | }
33 | }
34 | return .other
35 | }
36 |
37 | func textDiff(_ inserted: String, against deleted: String, defaults: DefaultManager) -> NSAttributedString {
38 | let attributes = [NSAttributedStringKey.foregroundColor: defaults.extraColor]
39 | let attributed = NSMutableAttributedString()
40 |
41 | for diff in diff_diffsBetweenTexts(deleted, inserted) {
42 | let diff = diff as! DMDiff
43 | if diff.operation == DIFF_INSERT {
44 | continue
45 | }
46 |
47 | let next = NSMutableAttributedString(string: diff.text ?? "")
48 | if diff.operation == DIFF_DELETE {
49 | next.setAttributes(attributes, range: NSMakeRange(0, next.length))
50 | }
51 |
52 | attributed.append(next)
53 | }
54 |
55 | return attributed
56 | }
57 |
58 | open func generateHighlights(sequence: AnySequence, defaults: DefaultManager) -> LNFileHighlights {
59 | let deletedColor = defaults.deletedColor
60 | let modifiedColor = defaults.modifiedColor
61 | let addedColor = defaults.addedColor
62 |
63 | var currentLine = 0, startLine = 0, deletedCount = 0, insertedText = ""
64 | let fileHighlights = LNFileHighlights()
65 | var element: LNHighlightElement?
66 |
67 | func closeRange() {
68 | element?.range = "\(startLine) \(currentLine - startLine)"
69 | if let deleted = element?.text {
70 | element?.setAttributedText(textDiff(insertedText, against: deleted, defaults: defaults))
71 | }
72 | }
73 |
74 | for line in sequence {
75 |
76 | switch delta(line: line) {
77 | case .start(let lineno):
78 | currentLine = lineno
79 | break
80 | case .delete(let text):
81 | if element == nil {
82 | startLine = currentLine
83 | element = LNHighlightElement()
84 | element?.start = currentLine
85 | element?.color = modifiedColor
86 | element?.text = ""
87 | fileHighlights[currentLine] = element
88 | }
89 | element?.text = (element?.text ?? "") + text + "\n"
90 | deletedCount += 1
91 | break
92 | case .insert(let text):
93 | if element == nil || currentLine - startLine >= deletedCount && element?.color != addedColor {
94 | if element == nil {
95 | startLine = currentLine
96 | }
97 | closeRange()
98 | element = LNHighlightElement()
99 | element?.start = currentLine
100 | element?.color = addedColor
101 | }
102 | fileHighlights[currentLine] = element
103 | insertedText += text
104 | currentLine += 1
105 | break
106 | case .other:
107 | if element?.color == modifiedColor && currentLine == startLine {
108 | element?.color = deletedColor
109 | }
110 | closeRange()
111 | currentLine += 1
112 | insertedText = ""
113 | deletedCount = 0
114 | element = nil
115 | break
116 | }
117 | }
118 |
119 | return fileHighlights
120 | }
121 |
122 | }
123 |
124 | extension NSTextCheckingResult {
125 |
126 | func group(_ group: Int, in string: String) -> String? {
127 | if range(at: group).location != NSNotFound {
128 | return string[range(at: group)]
129 | }
130 | return nil
131 | }
132 |
133 | }
134 |
135 | extension String {
136 |
137 | public subscript(i: Int) -> String {
138 | return self[i ..< i + 1]
139 | }
140 |
141 | public subscript(range: NSRange) -> String {
142 | return Range(range, in: self).flatMap { String(self[$0]) } ?? "??"
143 | }
144 |
145 | public subscript(r: Range) -> String {
146 | return self[NSMakeRange(r.lowerBound, r.upperBound - r.upperBound)]
147 | }
148 |
149 | }
150 |
--------------------------------------------------------------------------------
/GitDiffImpl/GitDiffImpl-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "LNExtensionProtocol.h"
6 | #import "LNFileHighlights.h"
7 |
8 | #import "DiffMatchPatch.h"
9 | #import "DMDiff.h"
10 |
--------------------------------------------------------------------------------
/GitDiffImpl/GitDiffImpl.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/GitDiffImpl/GitDiffImpl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitDiffImpl.swift
3 | // LNProvider
4 | //
5 | // Created by John Holdsworth on 31/03/2017.
6 | // Copyright © 2017 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | let implementationFactory = GitDiffImpl.self
12 | var lineNumberDefaults = DefaultManager()
13 | var diffgen = DiffProcessor()
14 |
15 | open class GitDiffImpl: LNExtensionBase, LNExtensionService {
16 |
17 | open override func getConfig(_ callback: @escaping LNConfigCallback) {
18 | callback([
19 | LNPopoverColorKey: lineNumberDefaults.popoverColor.stringRepresentation,
20 | LNApplyTitleKey: "GitDiff",
21 | LNApplyPromptKey: "Revert code at lines %d-%d to staged version?",
22 | LNApplyConfirmKey: "Revert",
23 | ])
24 | }
25 |
26 | open func requestHighlights(forFile filepath: String, callback: @escaping LNHighlightCallback) {
27 | DispatchQueue.global().async {
28 | let url = URL(fileURLWithPath: filepath)
29 | var arguments = ["git", "diff", "--no-ext-diff", "--no-color"]
30 | if lineNumberDefaults.showHead {
31 | arguments.append("HEAD")
32 | }
33 | arguments.append(url.lastPathComponent)
34 | let generator = TaskGenerator(launchPath: "/usr/bin/env", arguments: arguments,
35 | directory: url.deletingLastPathComponent().path)
36 |
37 | for _ in 0 ..< 4 {
38 | _ = generator.next()
39 | }
40 |
41 | let highlights = diffgen.generateHighlights(sequence: generator.lineSequence, defaults: lineNumberDefaults)
42 | callback(highlights.jsonData(), nil)
43 | }
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/GitDiffImpl/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | GitDiffImpl
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | XPC!
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | NSHumanReadableCopyright
24 | Copyright © 2017 John Holdsworth. All rights reserved.
25 | XPCService
26 |
27 | ServiceType
28 | Application
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/GitDiffRelay/GitDiffRelay-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "LNExtensionProtocol.h"
6 |
7 | #define EXTENSION_IMPL_SERVICE "com.johnholdsworth.GitDiffImpl"
8 |
--------------------------------------------------------------------------------
/GitDiffRelay/GitDiffRelay.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.network.client
8 |
9 | com.apple.security.network.server
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/GitDiffRelay/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | GitDiffRelay
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | XPC!
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | NSHumanReadableCopyright
24 | Copyright © 2017 John Holdsworth. All rights reserved.
25 | XPCService
26 |
27 | ServiceType
28 | Application
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/InferImpl/InferImpl-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "LNExtensionProtocol.h"
6 | #import "LNFileHighlights.h"
7 |
8 | #import "DiffMatchPatch.h"
9 | #import "DMDiff.h"
10 |
--------------------------------------------------------------------------------
/InferImpl/InferImpl.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FormatImpl.swift
3 | // LNProvider
4 | //
5 | // Created by John Holdsworth on 03/04/2017.
6 | // Copyright © 2017 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | let implementationFactory = InferImpl.self
12 | var diffgen = DiffProcessor()
13 |
14 | class InferDefaults: DefaultManager {
15 |
16 | open override var modifiedKey: String { return inferKey }
17 |
18 | }
19 |
20 | open class InferImpl: LNExtensionBase, LNExtensionService {
21 |
22 | open var defaults: DefaultManager {
23 | return InferDefaults()
24 | }
25 |
26 | open override func getConfig(_ callback: @escaping LNConfigCallback) {
27 | callback([
28 | LNApplyTitleKey: "Infer Types",
29 | LNApplyPromptKey: "Make type explicit",
30 | LNApplyConfirmKey: "Modify",
31 | ])
32 | }
33 |
34 | open func requestHighlights(forFile filepath: String, callback: @escaping LNHighlightCallback) {
35 | let url = URL(fileURLWithPath: filepath)
36 |
37 | guard url.pathExtension == "swift" else {
38 | callback(nil, nil)
39 | return
40 | }
41 |
42 | guard let script = Bundle.main.path(forResource: "infer", ofType: "sh") else {
43 | callback(nil, error(description: "script infer.sh not in XPC bundle"))
44 | return
45 | }
46 |
47 | DispatchQueue.global().async {
48 | let generator = TaskGenerator(launchPath: script, arguments: [filepath],
49 | directory: url.deletingLastPathComponent().path)
50 |
51 | for _ in 0 ..< 2 {
52 | _ = generator.next()
53 | }
54 |
55 | let highlights = diffgen.generateHighlights(sequence: generator.lineSequence, defaults: self.defaults)
56 | callback(highlights.jsonData(), nil)
57 | }
58 | }
59 |
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/InferImpl/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | InferImpl
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | XPC!
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | NSHumanReadableCopyright
24 | Copyright © 2017 John Holdsworth. All rights reserved.
25 | XPCService
26 |
27 | ServiceType
28 | Application
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/InferImpl/infer.mm:
--------------------------------------------------------------------------------
1 | //
2 | // infer.mm
3 | // infer
4 | //
5 | // Created by John Holdsworth on 21/08/2017.
6 | // Copyright © 2017 John Holdsworth. All rights reserved.
7 | //
8 | // Makes explicit, inferred types in a Swift source.
9 | // Requires project to have been built & indexed.
10 | //
11 |
12 | #import
13 | #import
14 | #import
15 |
16 | #import "sourcekitd.h"
17 |
18 | @interface PhaseOneFindArguemnts : NSObject
19 | - (NSString * _Nullable)projectForSourceFile:(NSString *)sourceFile;
20 | - (NSString * _Nullable)logDirectoryForProject:(NSString *)projectPath;
21 | - (NSString * _Nullable)commandLineForPrimaryFile:(NSString *)sourceFile
22 | inLogDirectory:(NSString *)logDirectory;
23 | @end
24 |
25 | #define INError(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
26 | //#define INError(fmt, ...) NSLog(@fmt, __VA_ARGS__)
27 |
28 | @interface PhaseTwoInferAssignments : NSObject
29 | - (int)inferAssignmentsFor:(const char *)sourceFile
30 | arguments:(const char **)argv into:(FILE *)output;
31 | @end
32 |
33 | int main(int argc, const char * argv[]) {
34 | const char *inferBinary = argv[0];
35 | if (argc < 2) {
36 | INError("Usage: %s \n", inferBinary);
37 | exit(1);
38 | }
39 | else if(argc == 2) {
40 | auto logReader = [PhaseOneFindArguemnts new];
41 | NSString *sourceFile = [NSString stringWithUTF8String:argv[1]];
42 | NSString *projectPath = [logReader projectForSourceFile:sourceFile];
43 | if (!projectPath) {
44 | INError("Could not find project for source file: %s\n", argv[1]);
45 | exit(1);
46 | }
47 |
48 | NSString *logDirectory = [logReader logDirectoryForProject:projectPath];
49 | NSString *compileCommand = [logReader commandLineForPrimaryFile:sourceFile
50 | inLogDirectory:logDirectory];
51 | if (!compileCommand) {
52 | INError("Could not find compile command for '%s' in log directory: %s\n",
53 | argv[1], logDirectory.UTF8String);
54 | exit(1);
55 | }
56 |
57 | NSTask *task = [NSTask new];
58 | task.launchPath = @"/bin/bash";
59 | task.arguments = @[@"-c", [NSString stringWithFormat:@"\"%@\" \"%@\" %@",
60 | [NSString stringWithUTF8String:inferBinary],
61 | sourceFile, compileCommand]];
62 | [task launch];
63 | [task waitUntilExit];
64 | exit(task.terminationStatus);
65 | }
66 | else {
67 | auto inferer = [PhaseTwoInferAssignments new];
68 | exit([inferer inferAssignmentsFor:argv[1] arguments:argv + 4 into:stdout]);
69 | }
70 | }
71 |
72 | @implementation NSTask(FILE)
73 |
74 | - (FILE *)stdout {
75 | NSPipe *pipe = [NSPipe pipe];
76 | self.standardOutput = pipe;
77 | int fd = [self.standardOutput fileHandleForReading].fileDescriptor;
78 | [self launch];
79 | return fdopen(fd, "r");
80 | }
81 |
82 | @end
83 |
84 | @implementation PhaseOneFindArguemnts {
85 | char logFile[PATH_MAX], commandBuffer[1024*1024]; // max command is 256k on Darwin
86 | }
87 |
88 | - (NSString *)fileWithExtension:(NSString *)extension inFiles:(NSArray *)files {
89 | for (NSString *file in files)
90 | if ([[file pathExtension] isEqualToString:extension])
91 | return file;
92 | return nil;
93 | }
94 |
95 | - (NSString *)projectForSourceFile:(NSString *)sourceFile {
96 | NSString *directory = sourceFile.stringByDeletingLastPathComponent;
97 | if ([directory isEqualToString:@"/"])
98 | return nil;
99 |
100 | NSFileManager *manager = [NSFileManager defaultManager];
101 | NSArray *fileList = [manager contentsOfDirectoryAtPath:directory error:NULL];
102 |
103 | if (NSString *projectFile =
104 | [self fileWithExtension:@"xcworkspace" inFiles:fileList] ?:
105 | [self fileWithExtension:@"xcodeproj" inFiles:fileList]) {
106 | NSString *projectPath = [directory stringByAppendingPathComponent:projectFile];
107 | NSString *logDir = [self logDirectoryForProject:projectPath];
108 | if ([manager fileExistsAtPath:logDir])
109 | return projectPath;
110 | }
111 |
112 | return [self projectForSourceFile:directory];
113 | }
114 |
115 | - (NSString *)logDirectoryForProject:(NSString *)projectPath {
116 | NSString *projectName = [projectPath.lastPathComponent stringByDeletingPathExtension];
117 | return [NSString stringWithFormat:@"%@/Library/Developer/Xcode/DerivedData/%@-%@/Logs/Build",
118 | NSHomeDirectory(), [projectName stringByReplacingOccurrencesOfString:@" " withString:@"_"],
119 | [self hashStringForPath:projectPath]];
120 | }
121 |
122 | - (NSString *)commandLineForPrimaryFile:(NSString *)sourceFile
123 | inLogDirectory:(NSString *)logDirectory {
124 | NSTask *lsTask = [NSTask new];
125 | lsTask.launchPath = @"/bin/bash";
126 | lsTask.arguments = @[@"-c", [NSString stringWithFormat:@"/bin/ls -t \"%@\"/*.xcactivitylog",
127 | logDirectory]];
128 | FILE *logsFILE = [lsTask stdout];
129 |
130 | while (fgets(logFile, sizeof logFile, logsFILE)) {
131 | logFile[strlen(logFile)-1] = '\000';
132 |
133 | NSTask *grepTask = [NSTask new];
134 | grepTask.launchPath = @"/bin/bash";
135 | grepTask.arguments = @[@"-c", [NSString stringWithFormat:@"/usr/bin/gunzip <'%@' | "
136 | "tr '\\r' '\\n' | /usr/bin/grep -E ' -primary-file \"?%@\"? '",
137 | [NSString stringWithUTF8String:logFile],
138 | [sourceFile stringByReplacingOccurrencesOfString:@"+" withString:@"\\+"]]];
139 | FILE *grepFILE = [grepTask stdout];
140 |
141 | if (fgets(commandBuffer, sizeof commandBuffer, grepFILE)) {
142 | *strstr(commandBuffer, " -o ") = '\000';
143 | [grepTask terminate];
144 | [lsTask terminate];
145 | return [NSString stringWithUTF8String:commandBuffer];
146 | }
147 |
148 | [grepTask waitUntilExit];
149 | pclose(grepFILE);
150 | }
151 |
152 | [lsTask waitUntilExit];
153 | pclose(logsFILE);
154 | return nil;
155 | }
156 |
157 | // Thanks to: http://samdmarshall.com/blog/xcode_deriveddata_hashes.html
158 |
159 | // this function is used to swap byte ordering of a 64bit integer
160 | uint64_t swap_uint64(uint64_t val) {
161 | val = ((val << 8) & 0xFF00FF00FF00FF00ULL) | ((val >> 8) & 0x00FF00FF00FF00FFULL);
162 | val = ((val << 16) & 0xFFFF0000FFFF0000ULL) | ((val >> 16) & 0x0000FFFF0000FFFFULL);
163 | return (val << 32) | (val >> 32);
164 | }
165 |
166 | /*!
167 | @method hashStringForPath
168 |
169 | Create the unique identifier string for a Xcode project path
170 |
171 | @param path (input) string path to the ".xcodeproj" or ".xcworkspace" file
172 |
173 | @result NSString* of the identifier
174 | */
175 | - (NSString *)hashStringForPath:(NSString *)path;
176 | {
177 | // using uint64_t[2] for ease of use, since it is the same size as char[CC_MD5_DIGEST_LENGTH]
178 | uint64_t digest[CC_MD2_DIGEST_LENGTH] = {0};
179 |
180 | // char array that will contain the identifier
181 | unsigned char resultStr[28] = {0};
182 |
183 | // setup md5 context
184 | CC_MD5_CTX md5;
185 | CC_MD5_Init(&md5);
186 |
187 | // get the UTF8 string of the path
188 | const char *c_path = [path UTF8String];
189 |
190 | // get length of the path string
191 | unsigned long length = strlen(c_path);
192 |
193 | // update the md5 context with the full path string
194 | CC_MD5_Update (&md5, c_path, (CC_LONG)length);
195 |
196 | // finalize working with the md5 context and store into the digest
197 | CC_MD5_Final ((unsigned char *)digest, &md5);
198 |
199 | // take the first 8 bytes of the digest and swap byte order
200 | uint64_t startValue = swap_uint64(digest[0]);
201 |
202 | // for indexes 13->0
203 | int index = 13;
204 | do {
205 | // take 'startValue' mod 26 (restrict to alphabetic) and add based 'a'
206 | resultStr[index] = (char)((startValue % 26) + 'a');
207 |
208 | // divide 'startValue' by 26
209 | startValue /= 26;
210 |
211 | index--;
212 | } while (index >= 0);
213 |
214 | // The second loop, this time using the last 8 bytes
215 | // repeating the same process as before but over indexes 27->14
216 | startValue = swap_uint64(digest[1]);
217 | index = 27;
218 | do {
219 | resultStr[index] = (char)((startValue % 26) + 'a');
220 | startValue /= 26;
221 | index--;
222 | } while (index > 13);
223 |
224 | // create a new string from the 'resultStr' char array and return
225 | return [[NSString alloc] initWithBytes:resultStr length:28 encoding:NSUTF8StringEncoding];
226 | }
227 |
228 | @end
229 |
230 | @implementation PhaseTwoInferAssignments
231 |
232 | - (int)inferAssignmentsFor:(const char *)sourceFile arguments:(const char **)argv into:(FILE *)output {
233 | NSError *error;
234 | NSMutableData *sourceData = [NSMutableData dataWithContentsOfFile:[NSString stringWithUTF8String:sourceFile]
235 | options:0 error:&error];
236 | if (!sourceData) {
237 | INError("Could not load source file '%s': %s\n",
238 | sourceFile, error.localizedDescription.UTF8String);
239 | return 1;
240 | }
241 |
242 | const char *input = (const char *)[sourceData bytes], eos = '\000', *next = input;
243 | [sourceData appendBytes:&eos length:sizeof eos];
244 |
245 | sourcekitd_initialize();
246 |
247 | int argc = 0, argo = 0;
248 | sourcekitd_object_t objects[1000];
249 | NSDictionary *skips = @{
250 | @"-primary-file": @1,
251 | @"-emit-module-doc-path": @2,
252 | @"-emit-dependencies-path": @2,
253 | @"-emit-reference-dependencies-path": @2,
254 | @"-enable-objc-interop": @1,
255 | @"-warn-long-function-bodies": @1,
256 | @"-warn-long-expression-type-checking": @1,
257 | @"-serialize-debugging-options": @1,
258 | @"-enable-anonymous-context-mangled-names": @1,
259 | @"-pch-disable-validation": @1,
260 | @"-serialize-diagnostics-path": @2,
261 | @"-target-sdk-version": @2,
262 | };
263 | while (argv[argc]) {
264 | NSString *option = [NSString stringWithUTF8String:argv[argc]];
265 | option = [option stringByReplacingOccurrencesOfString:@"=.*" withString:@""
266 | options:NSRegularExpressionSearch range:NSMakeRange(0, option.length)];
267 | int skip = [skips[option] intValue];
268 | if (!skip)
269 | objects[argo++] =
270 | sourcekitd_request_string_create(argv[argc]);
271 | argc += skip ?: 1;
272 | }
273 |
274 | sourcekitd_object_t compilerArgs =
275 | sourcekitd_request_array_create(objects, argo);
276 |
277 | sourcekitd_uid_t nameID = sourcekitd_uid_get_from_cstr("key.name");
278 | sourcekitd_uid_t requestID = sourcekitd_uid_get_from_cstr("key.request");
279 | sourcekitd_uid_t sourceFileID = sourcekitd_uid_get_from_cstr("key.sourcefile");
280 | sourcekitd_uid_t compilerArgsID = sourcekitd_uid_get_from_cstr("key.compilerargs");
281 | sourcekitd_uid_t cursorRequestID = sourcekitd_uid_get_from_cstr("source.request.cursorinfo");
282 |
283 | sourcekitd_object_t cursorRequest = sourcekitd_request_dictionary_create(nil, nil, 0);
284 | sourcekitd_request_dictionary_set_uid(cursorRequest, requestID, cursorRequestID);
285 | sourcekitd_request_dictionary_set_string(cursorRequest, sourceFileID, sourceFile);
286 | sourcekitd_request_dictionary_set_string(cursorRequest, nameID, sourceFile);
287 | sourcekitd_request_dictionary_set_value(cursorRequest, compilerArgsID, compilerArgs);
288 |
289 | sourcekitd_uid_t offsetID = sourcekitd_uid_get_from_cstr("key.offset");
290 | sourcekitd_uid_t declID = sourcekitd_uid_get_from_cstr("key.fully_annotated_decl");
291 |
292 | // sourcekit cursor ops deal in byte offsets
293 | regex_t assigns;
294 | if (int err = regcomp(&assigns,
295 | "[ \t\n](let|var)[ \t]+([^\n,)]+?)[ \t]=[ \t]",
296 | REG_EXTENDED|REG_ENHANCED)) {
297 | char errbuff[1000];
298 | regerror(err, &assigns, errbuff, sizeof errbuff);
299 | INError("Regex compilation error: %s\n", errbuff);
300 | return 1;
301 | }
302 |
303 | regmatch_t matches[3];
304 |
305 | while(regexec(&assigns, next, sizeof matches/sizeof matches[0], matches, 0) != REG_NOMATCH) {
306 | const char *varStart = next + matches[2].rm_so, *equals = next + matches[2].rm_eo;
307 | ptrdiff_t byteOffset = varStart - input;
308 |
309 | next += fwrite((void *)next, 1, matches[1].rm_so, output);
310 |
311 | sourcekitd_request_dictionary_set_int64(cursorRequest, offsetID, byteOffset);
312 |
313 | sourcekitd_response_t response = sourcekitd_send_request_sync(cursorRequest);
314 | if (sourcekitd_response_is_error(response)) {
315 | NSLog(@"Cursor request %s",
316 | sourcekitd_response_error_get_description(response));
317 | sourcekitd_request_description_dump(cursorRequest);
318 | continue;
319 | }
320 | else {
321 | sourcekitd_variant_t dict = sourcekitd_response_get_value(response);
322 | if (const char *declaration = sourcekitd_variant_dictionary_get_string(dict, declID)) {
323 | const char *replacement = strstr(declaration, "let") ?:
324 | strstr(declaration, "var") ?: "NODECL";
325 | int inTag = 0;
326 | while (char ch = *replacement++) {
327 | switch (ch) {
328 | case '<': case '{':
329 | inTag++;
330 | break;
331 | case '>': case '}':
332 | inTag--;
333 | break;
334 | case '&':
335 | if (strncmp(replacement, "lt;", 3) == 0) {
336 | fputc('<', output);
337 | replacement += 3;
338 | break;
339 | }
340 | else if (strncmp(replacement, "gt;", 3) == 0) {
341 | fputc('>', output);
342 | replacement += 3;
343 | break;
344 | }
345 | default:
346 | if (!inTag && !(ch == ' ' &&
347 | (*replacement == ':' || *replacement == ' ' || *replacement == '{')))
348 | fputc(ch, output);
349 | }
350 | }
351 | }
352 | else {
353 | fwrite((void *)next, 1, equals - next, output);
354 | }
355 | }
356 |
357 | sourcekitd_response_dispose(response);
358 | next = equals;
359 | }
360 |
361 | fwrite((void *)next, 1, strlen(next), output);
362 | fflush(output);
363 | return 0;
364 | }
365 |
366 | @end
367 |
--------------------------------------------------------------------------------
/InferImpl/infer.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # infer.sh
4 | # LNProvider
5 | #
6 | # Created by User on 22/08/2017.
7 | # Copyright © 2017 John Holdsworth. All rights reserved.
8 |
9 | diff -Naur <("$(dirname "$0")/infer" "$1") "$1"
10 |
11 |
--------------------------------------------------------------------------------
/InferImpl/sourcekitd.h:
--------------------------------------------------------------------------------
1 | //===--- sourcekitd.h - -----------------------------------------*- C++ -*-===//
2 | //
3 | // This source file is part of the Swift.org open source project
4 | //
5 | // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6 | // Licensed under Apache License v2.0 with Runtime Library Exception
7 | //
8 | // See https://swift.org/LICENSE.txt for license information
9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10 | //
11 | //===----------------------------------------------------------------------===//
12 |
13 | #ifndef LLVM_SOURCEKITD_SOURCEKITD_H
14 | #define LLVM_SOURCEKITD_SOURCEKITD_H
15 |
16 | #include
17 | #include
18 | #include
19 |
20 | /// \brief The version constants for the sourcekitd API.
21 | /// SOURCEKITD_VERSION_MINOR should increase when there are API additions.
22 | /// SOURCEKITD_VERSION_MAJOR is intended for "major" source/ABI breaking
23 | /// changes.
24 | ///
25 | /// The policy about the sourcekitd API is to keep it source and ABI compatible,
26 | /// thus SOURCEKITD_VERSION_MAJOR is expected to remain stable.
27 | #define SOURCEKITD_VERSION_MAJOR 0
28 | #define SOURCEKITD_VERSION_MINOR 3
29 |
30 | #define SOURCEKITD_VERSION_ENCODE(major, minor) ( \
31 | ((major) * 10000) \
32 | + ((minor) * 1))
33 |
34 | #define SOURCEKITD_VERSION SOURCEKITD_VERSION_ENCODE( \
35 | SOURCEKITD_VERSION_MAJOR, \
36 | SOURCEKITD_VERSION_MINOR)
37 |
38 | #define SOURCEKITD_VERSION_STRINGIZE_(major, minor) \
39 | #major"."#minor
40 | #define SOURCEKITD_VERSION_STRINGIZE(major, minor) \
41 | SOURCEKITD_VERSION_STRINGIZE_(major, minor)
42 |
43 | #define SOURCEKITD_VERSION_STRING SOURCEKITD_VERSION_STRINGIZE( \
44 | SOURCEKITD_VERSION_MAJOR, \
45 | SOURCEKITD_VERSION_MINOR)
46 |
47 | #ifdef __cplusplus
48 | # define SOURCEKITD_BEGIN_DECLS extern "C" {
49 | # define SOURCEKITD_END_DECLS }
50 | #else
51 | # define SOURCEKITD_BEGIN_DECLS
52 | # define SOURCEKITD_END_DECLS
53 | #endif
54 |
55 | #ifndef SOURCEKITD_PUBLIC
56 | # if defined (_MSC_VER)
57 | # define SOURCEKITD_PUBLIC __declspec(dllimport)
58 | # else
59 | # define SOURCEKITD_PUBLIC
60 | # endif
61 | #endif
62 |
63 | #ifndef __has_feature
64 | # define __has_feature(x) 0
65 | #endif
66 |
67 | #if __has_feature(blocks)
68 | # define SOURCEKITD_HAS_BLOCKS 1
69 | #else
70 | # define SOURCEKITD_HAS_BLOCKS 0
71 | #endif
72 |
73 | #if defined(__clang__) || defined(__GNUC__)
74 | # define SOURCEKITD_WARN_RESULT __attribute__((__warn_unused_result__))
75 | # define SOURCEKITD_NONNULL1 __attribute__((__nonnull__(1)))
76 | # define SOURCEKITD_NONNULL2 __attribute__((__nonnull__(2)))
77 | # define SOURCEKITD_NONNULL3 __attribute__((__nonnull__(3)))
78 | # define SOURCEKITD_NONNULL_ALL __attribute__((__nonnull__))
79 | # define SOURCEKITD_DEPRECATED(m) __attribute__((deprecated(m)))
80 | #else
81 | # define SOURCEKITD_WARN_RESULT
82 | # define SOURCEKITD_NONNULL1
83 | # define SOURCEKITD_NONNULL2
84 | # define SOURCEKITD_NONNULL3
85 | # define SOURCEKITD_NONNULL_ALL
86 | #endif
87 |
88 | SOURCEKITD_BEGIN_DECLS
89 |
90 | /// \brief Initializes structures needed across the rest of the sourcekitd API.
91 | ///
92 | /// Must be called before any other sourcekitd call.
93 | /// Can be called multiple times as long as it is matched with a
94 | /// \c sourcekitd_shutdown call.
95 | /// Calling \c sourcekitd_initialize a second time without an intervening
96 | /// \c sourcekitd_shutdown is undefined.
97 | /// \c sourcekitd_initialize does not need to be called again even if the
98 | /// service crashes.
99 | SOURCEKITD_PUBLIC
100 | void
101 | sourcekitd_initialize(void);
102 |
103 | /// \brief Deallocates structures needed across the rest of the sourcekitd API.
104 | ///
105 | /// If there are response handlers still waiting for a response, they will
106 | /// receive a SOURCEKITD_ERROR_REQUEST_CANCELLED response.
107 | ///
108 | /// Calling \c sourcekitd_shutdown without a matching \c sourcekitd_initialize
109 | /// is undefined.
110 | SOURCEKITD_PUBLIC
111 | void
112 | sourcekitd_shutdown(void);
113 |
114 | #if SOURCEKITD_HAS_BLOCKS
115 |
116 | typedef void(^sourcekitd_interrupted_connection_handler_t)(void);
117 |
118 | /// \brief Sets the handler which should be called whenever the connection to
119 | /// SourceKit is interrupted.
120 | ///
121 | /// The handler should reestablish any necessary state, such as re-opening any
122 | /// documents which were open before the connection was interrupted.
123 | ///
124 | /// It is not necessary to call \c sourcekitd_initialize; the connection will
125 | /// automatically be reestablished when sending the next request.
126 | ///
127 | /// \param handler Interrupted connection handler to use. Pass NULL to remove
128 | /// the handler.
129 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL
130 | void
131 | sourcekitd_set_interrupted_connection_handler(
132 | sourcekitd_interrupted_connection_handler_t handler);
133 |
134 | #endif
135 |
136 | /// \brief A "unique identifier" utilized by the request/response protocol.
137 | ///
138 | /// A \c sourcekitd_uid_t object is associated with a string and is uniqued for
139 | /// the lifetime of the process. Its usefulness is in providing an "infinite
140 | /// namespace" of identifiers.
141 | /// A \c sourcekitd_uid_t object remains valid even if the service crashes.
142 | typedef struct sourcekitd_uid_s *sourcekitd_uid_t;
143 |
144 | /// \brief Create a \c sourcekitd_uid_t from a C string.
145 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1 SOURCEKITD_WARN_RESULT
146 | sourcekitd_uid_t
147 | sourcekitd_uid_get_from_cstr(const char *string);
148 |
149 | /// \brief Create a \c sourcekitd_uid_t from a string buffer.
150 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1 SOURCEKITD_WARN_RESULT
151 | sourcekitd_uid_t
152 | sourcekitd_uid_get_from_buf(const char *buf, size_t length);
153 |
154 | /// \brief Get the length of the string associated with a \c sourcekitd_uid_t.
155 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1 SOURCEKITD_WARN_RESULT
156 | size_t
157 | sourcekitd_uid_get_length(sourcekitd_uid_t obj);
158 |
159 | /// \brief Get the C string pointer associated with a \c sourcekitd_uid_t.
160 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1 SOURCEKITD_WARN_RESULT
161 | const char *
162 | sourcekitd_uid_get_string_ptr(sourcekitd_uid_t obj);
163 |
164 | /// \defgroup Request API
165 | ///
166 | /// @{
167 | ///
168 | /// \brief Used for constructing a request object.
169 | typedef void *sourcekitd_object_t;
170 |
171 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1
172 | sourcekitd_object_t
173 | sourcekitd_request_retain(sourcekitd_object_t object);
174 |
175 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1
176 | void
177 | sourcekitd_request_release(sourcekitd_object_t object);
178 |
179 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
180 | sourcekitd_object_t
181 | sourcekitd_request_dictionary_create(const sourcekitd_uid_t *keys,
182 | const sourcekitd_object_t *values,
183 | size_t count);
184 |
185 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL
186 | void
187 | sourcekitd_request_dictionary_set_value(sourcekitd_object_t dict,
188 | sourcekitd_uid_t key,
189 | sourcekitd_object_t value);
190 |
191 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL
192 | void
193 | sourcekitd_request_dictionary_set_string(sourcekitd_object_t dict,
194 | sourcekitd_uid_t key,
195 | const char *string);
196 |
197 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL
198 | void
199 | sourcekitd_request_dictionary_set_stringbuf(sourcekitd_object_t dict,
200 | sourcekitd_uid_t key,
201 | const char *buf, size_t length);
202 |
203 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1 SOURCEKITD_NONNULL2
204 | void
205 | sourcekitd_request_dictionary_set_int64(sourcekitd_object_t dict,
206 | sourcekitd_uid_t key, int64_t val);
207 |
208 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL
209 | void
210 | sourcekitd_request_dictionary_set_uid(sourcekitd_object_t dict,
211 | sourcekitd_uid_t key,
212 | sourcekitd_uid_t uid);
213 |
214 | #define SOURCEKITD_ARRAY_APPEND ((size_t)(-1))
215 |
216 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
217 | sourcekitd_object_t
218 | sourcekitd_request_array_create(const sourcekitd_object_t *objects,
219 | size_t count);
220 |
221 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1 SOURCEKITD_NONNULL3
222 | void
223 | sourcekitd_request_array_set_value(sourcekitd_object_t array, size_t index,
224 | sourcekitd_object_t value);
225 |
226 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1 SOURCEKITD_NONNULL3
227 | void
228 | sourcekitd_request_array_set_string(sourcekitd_object_t array, size_t index,
229 | const char *string);
230 |
231 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1 SOURCEKITD_NONNULL3
232 | void
233 | sourcekitd_request_array_set_stringbuf(sourcekitd_object_t array, size_t index,
234 | const char *buf, size_t length);
235 |
236 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1
237 | void
238 | sourcekitd_request_array_set_int64(sourcekitd_object_t array, size_t index,
239 | int64_t val);
240 |
241 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1 SOURCEKITD_NONNULL3
242 | void
243 | sourcekitd_request_array_set_uid(sourcekitd_object_t array, size_t index,
244 | sourcekitd_uid_t uid);
245 |
246 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
247 | sourcekitd_object_t
248 | sourcekitd_request_int64_create(int64_t val);
249 |
250 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1 SOURCEKITD_WARN_RESULT
251 | sourcekitd_object_t
252 | sourcekitd_request_string_create(const char *string);
253 |
254 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1 SOURCEKITD_WARN_RESULT
255 | sourcekitd_object_t
256 | sourcekitd_request_uid_create(sourcekitd_uid_t uid);
257 |
258 | /// \brief Creates a request object by parsing the provided string in YAML
259 | /// format.
260 | ///
261 | /// \param yaml The string in YAML format.
262 | ///
263 | /// \param error A pointer to store a C string of the error description if
264 | /// parsing fails. This string should be disposed of with \c free when done.
265 | /// Can be NULL.
266 | ///
267 | /// \returns A sourcekitd_object_t instance or NULL if parsing fails.
268 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1 SOURCEKITD_WARN_RESULT
269 | sourcekitd_object_t
270 | sourcekitd_request_create_from_yaml(const char *yaml, char **error);
271 |
272 | /// \brief Prints to stderr a string representation of the request object in
273 | /// YAML format.
274 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1
275 | void
276 | sourcekitd_request_description_dump(sourcekitd_object_t obj);
277 |
278 | /// \brief Copies a string representation of the request object in YAML format.
279 | /// \returns A string representation of the request object. This string should
280 | /// be disposed of with \c free when done.
281 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1
282 | char *
283 | sourcekitd_request_description_copy(sourcekitd_object_t obj);
284 |
285 | /// @}
286 |
287 | /// \defgroup Response API
288 | ///
289 | /// @{
290 |
291 | /// \brief The result of a request.
292 | ///
293 | /// If the request failed \c sourcekitd_response_t will be an error response and
294 | /// will contain information about the error, otherwise it will contain the
295 | /// resulting values of the request.
296 | typedef void *sourcekitd_response_t;
297 |
298 | /// \brief A value of the response object.
299 | ///
300 | /// Its lifetime is tied to the sourcekitd_response_t object that it came from.
301 | typedef struct {
302 | uint64_t data[3];
303 | } sourcekitd_variant_t;
304 |
305 | typedef enum {
306 | SOURCEKITD_VARIANT_TYPE_NULL = 0,
307 | SOURCEKITD_VARIANT_TYPE_DICTIONARY = 1,
308 | SOURCEKITD_VARIANT_TYPE_ARRAY = 2,
309 | SOURCEKITD_VARIANT_TYPE_INT64 = 3,
310 | SOURCEKITD_VARIANT_TYPE_STRING = 4,
311 | SOURCEKITD_VARIANT_TYPE_UID = 5,
312 | SOURCEKITD_VARIANT_TYPE_BOOL = 6
313 | } sourcekitd_variant_type_t;
314 |
315 | typedef enum {
316 | SOURCEKITD_ERROR_CONNECTION_INTERRUPTED = 1,
317 | SOURCEKITD_ERROR_REQUEST_INVALID = 2,
318 | SOURCEKITD_ERROR_REQUEST_FAILED = 3,
319 | SOURCEKITD_ERROR_REQUEST_CANCELLED = 4
320 | } sourcekitd_error_t;
321 |
322 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1
323 | void
324 | sourcekitd_response_dispose(sourcekitd_response_t obj);
325 |
326 | /// \brief Returns true if the given response is an error.
327 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL SOURCEKITD_WARN_RESULT
328 | bool
329 | sourcekitd_response_is_error(sourcekitd_response_t obj);
330 |
331 | /// \brief Returns the error kind given a response error.
332 | ///
333 | /// Passing a response object that is not an error will result in undefined
334 | /// behavior.
335 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL SOURCEKITD_WARN_RESULT
336 | sourcekitd_error_t
337 | sourcekitd_response_error_get_kind(sourcekitd_response_t err);
338 |
339 | /// \brief Returns a C string of the error description.
340 | ///
341 | /// Passing a response object that is not an error will result in undefined
342 | /// behavior.
343 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL SOURCEKITD_WARN_RESULT
344 | const char *
345 | sourcekitd_response_error_get_description(sourcekitd_response_t err);
346 |
347 | /// \brief Returns the value contained in the response.
348 | ///
349 | /// If the response is an error it will return a null variant.
350 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1 SOURCEKITD_WARN_RESULT
351 | sourcekitd_variant_t
352 | sourcekitd_response_get_value(sourcekitd_response_t resp);
353 |
354 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL SOURCEKITD_WARN_RESULT
355 | sourcekitd_variant_type_t
356 | sourcekitd_variant_get_type(sourcekitd_variant_t obj);
357 |
358 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL2 SOURCEKITD_WARN_RESULT
359 | sourcekitd_variant_t
360 | sourcekitd_variant_dictionary_get_value(sourcekitd_variant_t dict,
361 | sourcekitd_uid_t key);
362 |
363 | /// The underlying C string for the specified key. NULL if the value for the
364 | /// specified key is not a C string value or if there is no value for the
365 | /// specified key.
366 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL SOURCEKITD_WARN_RESULT
367 | const char *
368 | sourcekitd_variant_dictionary_get_string(sourcekitd_variant_t dict,
369 | sourcekitd_uid_t key);
370 |
371 | /// The underlying \c int64 value for the specified key. 0 if the
372 | /// value for the specified key is not an integer value or if there is no
373 | /// value for the specified key.
374 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL SOURCEKITD_WARN_RESULT
375 | int64_t
376 | sourcekitd_variant_dictionary_get_int64(sourcekitd_variant_t dict,
377 | sourcekitd_uid_t key);
378 |
379 | /// The underlying \c bool value for the specified key. false if the
380 | /// value for the specified key is not a Boolean value or if there is no
381 | /// value for the specified key.
382 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL SOURCEKITD_WARN_RESULT
383 | bool
384 | sourcekitd_variant_dictionary_get_bool(sourcekitd_variant_t dict,
385 | sourcekitd_uid_t key);
386 |
387 | /// The underlying \c sourcekitd_uid_t value for the specified key. NULL if the
388 | /// value for the specified key is not a uid value or if there is no
389 | /// value for the specified key.
390 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL SOURCEKITD_WARN_RESULT
391 | sourcekitd_uid_t
392 | sourcekitd_variant_dictionary_get_uid(sourcekitd_variant_t dict,
393 | sourcekitd_uid_t key);
394 |
395 | #if SOURCEKITD_HAS_BLOCKS
396 | /// \brief A block to be invoked for every key/value pair in the dictionary.
397 | ///
398 | /// \param key The current key in the iteration.
399 | ///
400 | /// \param value The current value in the iteration.
401 | ///
402 | /// \returns true to indicate that iteration should continue.
403 | typedef bool (^sourcekitd_variant_dictionary_applier_t)(sourcekitd_uid_t key,
404 | sourcekitd_variant_t value);
405 |
406 | /// \brief Invokes the given block for every key/value pair in the dictionary.
407 | ///
408 | /// \returns true to indicate that iteration of the dictionary completed
409 | /// successfully. Iteration will only fail if the applier block returns false.
410 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL
411 | bool
412 | sourcekitd_variant_dictionary_apply(sourcekitd_variant_t dict,
413 | sourcekitd_variant_dictionary_applier_t applier);
414 | #endif
415 |
416 | /// \brief A function to be invoked for every key/value pair in the dictionary.
417 | ///
418 | /// \param key The current key in the iteration.
419 | ///
420 | /// \param value The current value in the iteration.
421 | ///
422 | /// \returns true to indicate that iteration should continue.
423 | typedef bool (*sourcekitd_variant_dictionary_applier_f_t)(sourcekitd_uid_t key,
424 | sourcekitd_variant_t value,
425 | void *context);
426 |
427 | /// \brief Invokes the given function for every key/value pair in the
428 | /// dictionary.
429 | ///
430 | /// \returns true to indicate that iteration of the dictionary completed
431 | /// successfully. Iteration will only fail if the applier block returns 0.
432 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL2
433 | bool
434 | sourcekitd_variant_dictionary_apply_f(sourcekitd_variant_t dict,
435 | sourcekitd_variant_dictionary_applier_f_t applier,
436 | void *context);
437 |
438 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
439 | size_t
440 | sourcekitd_variant_array_get_count(sourcekitd_variant_t array);
441 |
442 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
443 | sourcekitd_variant_t
444 | sourcekitd_variant_array_get_value(sourcekitd_variant_t array, size_t index);
445 |
446 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
447 | const char *
448 | sourcekitd_variant_array_get_string(sourcekitd_variant_t array, size_t index);
449 |
450 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
451 | int64_t
452 | sourcekitd_variant_array_get_int64(sourcekitd_variant_t array, size_t index);
453 |
454 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
455 | bool
456 | sourcekitd_variant_array_get_bool(sourcekitd_variant_t array, size_t index);
457 |
458 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
459 | sourcekitd_uid_t
460 | sourcekitd_variant_array_get_uid(sourcekitd_variant_t array, size_t index);
461 |
462 | #if SOURCEKITD_HAS_BLOCKS
463 | /// \brief A block to be invoked for every value in the array.
464 | ///
465 | /// \param index The current index in the iteration.
466 | ///
467 | /// \param value The current value in the iteration.
468 | ///
469 | /// \returns true to indicate that iteration should continue.
470 | typedef bool (^sourcekitd_variant_array_applier_t)(size_t index,
471 | sourcekitd_variant_t value);
472 |
473 | /// \brief Invokes the given block for every value in the array.
474 | ///
475 | /// \returns true to indicate that iteration of the array completed
476 | /// successfully. Iteration will only fail if the applier block returns false.
477 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL
478 | bool
479 | sourcekitd_variant_array_apply(sourcekitd_variant_t array,
480 | sourcekitd_variant_array_applier_t applier);
481 | #endif
482 |
483 | /// \brief A function to be invoked for every value in the array.
484 | ///
485 | /// \param index The current index in the iteration.
486 | ///
487 | /// \param value The current value in the iteration.
488 | ///
489 | /// \returns true to indicate that iteration should continue.
490 | typedef bool (*sourcekitd_variant_array_applier_f_t)(size_t index,
491 | sourcekitd_variant_t value,
492 | void *context);
493 |
494 | /// \brief Invokes the given function for every value in the array.
495 | ///
496 | /// \returns true to indicate that iteration of the array completed
497 | /// successfully. Iteration will only fail if the applier block returns false.
498 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL2
499 | bool
500 | sourcekitd_variant_array_apply_f(sourcekitd_variant_t array,
501 | sourcekitd_variant_array_applier_f_t applier,
502 | void *context);
503 |
504 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
505 | int64_t
506 | sourcekitd_variant_int64_get_value(sourcekitd_variant_t obj);
507 |
508 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
509 | bool
510 | sourcekitd_variant_bool_get_value(sourcekitd_variant_t obj);
511 |
512 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
513 | size_t
514 | sourcekitd_variant_string_get_length(sourcekitd_variant_t obj);
515 |
516 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
517 | const char *
518 | sourcekitd_variant_string_get_ptr(sourcekitd_variant_t obj);
519 |
520 | SOURCEKITD_PUBLIC SOURCEKITD_WARN_RESULT
521 | sourcekitd_uid_t
522 | sourcekitd_variant_uid_get_value(sourcekitd_variant_t obj);
523 |
524 | /// \brief Prints to stderr a string representation of the response object in
525 | /// YAML format.
526 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1
527 | void
528 | sourcekitd_response_description_dump(sourcekitd_response_t resp);
529 |
530 | /// \brief Prints to the given file descriptor a string representation of the
531 | /// response object.
532 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1
533 | void
534 | sourcekitd_response_description_dump_filedesc(sourcekitd_response_t resp,
535 | int fd);
536 |
537 | /// \brief Copies a string representation of the response object in YAML format.
538 | /// \returns A string representation of the response object. This string should
539 | /// be disposed of with \c free when done.
540 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1
541 | char *
542 | sourcekitd_response_description_copy(sourcekitd_response_t resp);
543 |
544 | /// \brief Prints to stderr a string representation of the variant object in
545 | /// YAML format.
546 | SOURCEKITD_PUBLIC
547 | void
548 | sourcekitd_variant_description_dump(sourcekitd_variant_t obj);
549 |
550 | /// \brief Prints to the given file descriptor a string representation of the
551 | /// variant object.
552 | SOURCEKITD_PUBLIC
553 | void
554 | sourcekitd_variant_description_dump_filedesc(sourcekitd_variant_t obj, int fd);
555 |
556 | /// \brief Copies a string representation of the variant object in YAML format.
557 | /// \returns A string representation of the variant object. This string should
558 | /// be disposed of with \c free when done.
559 | SOURCEKITD_PUBLIC
560 | char *
561 | sourcekitd_variant_description_copy(sourcekitd_variant_t obj);
562 |
563 | /// \brief Copies a string representation of the variant object in JSON format.
564 | /// \returns A string representation of the variant object. This string should
565 | /// be disposed of with \c free when done.
566 | SOURCEKITD_PUBLIC
567 | char *
568 | sourcekitd_variant_json_description_copy(sourcekitd_variant_t obj);
569 |
570 | /// @}
571 |
572 | /// \brief Invoke a request synchronously.
573 | ///
574 | /// The caller accepts ownership of the returned sourcekitd_response_t object
575 | /// and should invoke \c sourcekitd_response_dispose on it when it is done with
576 | /// it.
577 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL_ALL SOURCEKITD_WARN_RESULT
578 | sourcekitd_response_t
579 | sourcekitd_send_request_sync(sourcekitd_object_t req);
580 |
581 | /// \brief Used to cancel a request that has been invoked asynchronously.
582 | typedef void *sourcekitd_request_handle_t;
583 |
584 | #if SOURCEKITD_HAS_BLOCKS
585 | /// \brief Receives the response of an asynchronous request or notification.
586 | ///
587 | /// The receiver accepts ownership of the response object and should invoke
588 | /// \c sourcekitd_response_dispose on it when it is done with it.
589 | typedef void (^sourcekitd_response_receiver_t)(sourcekitd_response_t resp);
590 |
591 | /// \brief Invoke a request asynchronously.
592 | ///
593 | /// \param req the request object.
594 | ///
595 | /// \param out_handle the address where the associated
596 | /// \c sourcekitd_request_handle_t will be stored. Can be NULL.
597 | ///
598 | /// \param receiver the block that will receive the response object.
599 | SOURCEKITD_PUBLIC SOURCEKITD_NONNULL1
600 | void
601 | sourcekitd_send_request(sourcekitd_object_t req,
602 | sourcekitd_request_handle_t *out_handle,
603 | sourcekitd_response_receiver_t receiver);
604 | #endif
605 |
606 | /// \brief Cancel a request using the associated request handle returned by
607 | /// \c sourcekitd_send_request.
608 | ///
609 | /// It is not guaranteed that invoking \c sourcekitd_cancel_request will cancel
610 | /// the request. If the request gets cancelled, the receiver will get a
611 | /// \c SOURCEKITD_ERROR_REQUEST_CANCELLED response error.
612 | ///
613 | /// Calling \c sourcekitd_cancel_request after the response object has been
614 | /// delivered will have no effect.
615 | SOURCEKITD_PUBLIC
616 | void
617 | sourcekitd_cancel_request(sourcekitd_request_handle_t handle);
618 |
619 | #if SOURCEKITD_HAS_BLOCKS
620 |
621 | /// \brief Sets the handler which should be called to receive notifications.
622 | /// The block will be set to be executed in the main thread queue.
623 | ///
624 | /// If the connection to SourceKit is interrupted the handler will receive an
625 | /// error response object of kind \c SOURCEKITD_ERROR_CONNECTION_INTERRUPTED.
626 | /// Any subsequent requests will immediately fail with the same error until
627 | /// the service is restored.
628 | /// When the service is restored the handler will receive an empty response
629 | /// object.
630 | ///
631 | /// \param receiver Notification handler block to use. Pass NULL to remove the
632 | /// previous handler that was set.
633 | SOURCEKITD_PUBLIC
634 | void
635 | sourcekitd_set_notification_handler(sourcekitd_response_receiver_t receiver);
636 |
637 | typedef sourcekitd_uid_t(^sourcekitd_uid_handler_t)(const char* uidStr);
638 |
639 | SOURCEKITD_PUBLIC SOURCEKITD_DEPRECATED("use sourcekitd_set_uid_handlers")
640 | void sourcekitd_set_uid_handler(sourcekitd_uid_handler_t handler);
641 |
642 | typedef sourcekitd_uid_t(^sourcekitd_uid_from_str_handler_t)(const char* uidStr);
643 | typedef const char *(^sourcekitd_str_from_uid_handler_t)(sourcekitd_uid_t uid);
644 |
645 | SOURCEKITD_PUBLIC
646 | void
647 | sourcekitd_set_uid_handlers(sourcekitd_uid_from_str_handler_t uid_from_str,
648 | sourcekitd_str_from_uid_handler_t str_from_uid);
649 |
650 | #endif
651 |
652 | SOURCEKITD_END_DECLS
653 |
654 | #endif
655 |
656 |
--------------------------------------------------------------------------------
/InferRelay/InferRelay-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "LNExtensionProtocol.h"
6 |
7 | #define EXTENSION_IMPL_SERVICE "com.johnholdsworth.InferImpl"
8 |
--------------------------------------------------------------------------------
/InferRelay/InferRelay.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/InferRelay/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | UnusedRelay
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | XPC!
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | NSHumanReadableCopyright
24 | Copyright © 2017 John Holdsworth. All rights reserved.
25 | XPCService
26 |
27 | ServiceType
28 | Application
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 John Holdsworth
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/LNProvider.t2d:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/LNProvider.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/LNProvider.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/LNProvider/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // LNProvider
4 | //
5 | // Created by John Holdsworth on 31/03/2017.
6 | // Copyright © 2017 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 |
14 | @IBOutlet weak var window: NSWindow!
15 | @IBOutlet var defaults: DefaultManager!
16 |
17 | @IBOutlet var formatChecked: NSButton!
18 | @IBOutlet var gitDiffChecked: NSButton!
19 | @IBOutlet var gitBlameChecked: NSButton!
20 | @IBOutlet var inferChecked: NSButton!
21 |
22 | var services = [LNExtensionClient]()
23 | private var statusItem: NSStatusItem!
24 |
25 | lazy var buttonMap: [NSButton: String] = [
26 | self.formatChecked: "com.johnholdsworth.FormatRelay",
27 | self.gitDiffChecked: "com.johnholdsworth.GitDiffRelay",
28 | self.gitBlameChecked: "com.johnholdsworth.GitBlameRelay",
29 | self.inferChecked: "com.johnholdsworth.InferRelay",
30 | ]
31 |
32 | func applicationDidFinishLaunching(_: Notification) {
33 | startServiceAndRegister(checkButton: formatChecked)
34 | startServiceAndRegister(checkButton: gitDiffChecked)
35 | startServiceAndRegister(checkButton: gitBlameChecked)
36 | startServiceAndRegister(checkButton: inferChecked)
37 | let statusBar = NSStatusBar.system
38 | statusItem = statusBar.statusItem(withLength: statusBar.thickness)
39 | statusItem.toolTip = "GitDiff Preferences"
40 | statusItem.highlightMode = true
41 | statusItem.target = self
42 | statusItem.action = #selector(show(sender:))
43 | statusItem.isEnabled = true
44 | statusItem.title = ""
45 | setMenuIcon(tiffName: "icon_16x16")
46 | NSColorPanel.shared.showsAlpha = true
47 | window.appearance = NSAppearance(named: NSAppearance.Name.vibrantDark)
48 | }
49 |
50 | func setMenuIcon(tiffName: String) {
51 | if let path = Bundle.main.path(forResource: tiffName, ofType: "tiff"),
52 | let image = NSImage(contentsOfFile: path) {
53 | image.isTemplate = true
54 | statusItem.image = image
55 | statusItem.alternateImage = statusItem.image
56 | }
57 | }
58 |
59 | @IBAction func show(sender: Any) {
60 | window.makeKeyAndOrderFront(sender)
61 | NSApp.activate(ignoringOtherApps: true)
62 | }
63 |
64 | func startServiceAndRegister(checkButton: NSButton) {
65 | if checkButton.state == .on, let serviceName = buttonMap[checkButton] {
66 | services.append(LNExtensionClient(serviceName: serviceName, delegate: nil))
67 | }
68 | }
69 |
70 | @IBAction func serviceDidChange(checkButton: NSButton) {
71 | if checkButton.state == .on {
72 | startServiceAndRegister(checkButton: checkButton)
73 | } else if let serviceName = buttonMap[checkButton] {
74 | services.first(where: { $0.serviceName == serviceName })?.deregister()
75 | services = services.filter { $0.serviceName != serviceName }
76 | }
77 | }
78 |
79 | func applicationWillTerminate(_: Notification) {
80 | _ = services.map { $0.deregister() }
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/LNProvider/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "16x16",
5 | "idiom" : "mac",
6 | "filename" : "icon_16x16.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "16x16",
11 | "idiom" : "mac",
12 | "filename" : "icon_16x16@2x.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "32x32",
17 | "idiom" : "mac",
18 | "filename" : "icon_32x32.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "32x32",
23 | "idiom" : "mac",
24 | "filename" : "icon_32x32@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "128x128",
29 | "idiom" : "mac",
30 | "filename" : "icon_128x128.png",
31 | "scale" : "1x"
32 | },
33 | {
34 | "size" : "128x128",
35 | "idiom" : "mac",
36 | "filename" : "icon_128x128@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "256x256",
41 | "idiom" : "mac",
42 | "filename" : "icon_256x256.png",
43 | "scale" : "1x"
44 | },
45 | {
46 | "size" : "256x256",
47 | "idiom" : "mac",
48 | "filename" : "icon_256x256@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "512x512",
53 | "idiom" : "mac",
54 | "filename" : "icon_512x512.png",
55 | "scale" : "1x"
56 | },
57 | {
58 | "size" : "512x512",
59 | "idiom" : "mac",
60 | "filename" : "icon_512x512@2x.png",
61 | "scale" : "2x"
62 | },
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/GitDiff/1089f1f4eadbb7297ffb598983402ac383ae3ddd/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_128x128.png
--------------------------------------------------------------------------------
/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/GitDiff/1089f1f4eadbb7297ffb598983402ac383ae3ddd/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_128x128@2x.png
--------------------------------------------------------------------------------
/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/GitDiff/1089f1f4eadbb7297ffb598983402ac383ae3ddd/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_16x16.png
--------------------------------------------------------------------------------
/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_16x16.tiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/GitDiff/1089f1f4eadbb7297ffb598983402ac383ae3ddd/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_16x16.tiff
--------------------------------------------------------------------------------
/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/GitDiff/1089f1f4eadbb7297ffb598983402ac383ae3ddd/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_16x16@2x.png
--------------------------------------------------------------------------------
/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/GitDiff/1089f1f4eadbb7297ffb598983402ac383ae3ddd/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_256x256.png
--------------------------------------------------------------------------------
/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/GitDiff/1089f1f4eadbb7297ffb598983402ac383ae3ddd/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_256x256@2x.png
--------------------------------------------------------------------------------
/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/GitDiff/1089f1f4eadbb7297ffb598983402ac383ae3ddd/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_32x32.png
--------------------------------------------------------------------------------
/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/GitDiff/1089f1f4eadbb7297ffb598983402ac383ae3ddd/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_32x32@2x.png
--------------------------------------------------------------------------------
/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/GitDiff/1089f1f4eadbb7297ffb598983402ac383ae3ddd/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_512x512.png
--------------------------------------------------------------------------------
/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/johnno1962/GitDiff/1089f1f4eadbb7297ffb598983402ac383ae3ddd/LNProvider/Assets.xcassets/AppIcon.appiconset/icon_512x512@2x.png
--------------------------------------------------------------------------------
/LNProvider/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/LNProvider/DefaultManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DefaultManager.swift
3 | // LNProvider
4 | //
5 | // Created by John Holdsworth on 03/04/2017.
6 | // Copyright © 2017 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | open class DefaultManager: NSObject {
12 |
13 | open var defaults = UserDefaults(suiteName: "LineNumber")!
14 |
15 | @IBOutlet weak var popoverColorWell: NSColorWell!
16 | @IBOutlet weak var deletedColorWell: NSColorWell!
17 | @IBOutlet weak var modifiedColorWell: NSColorWell!
18 | @IBOutlet weak var addedColorWell: NSColorWell!
19 | @IBOutlet weak var extraColorWell: NSColorWell!
20 | @IBOutlet weak var recentColorWell: NSColorWell!
21 | @IBOutlet weak var formatColorWell: NSColorWell!
22 | @IBOutlet weak var inferColorWell: NSColorWell!
23 |
24 | @IBOutlet weak var recentDaysField: NSTextField!
25 | @IBOutlet weak var formatIndentField: NSTextField!
26 |
27 | open var popoverKey: String { return "PopoverColor" }
28 | open var deletedKey: String { return "DeletedColor" }
29 | open var modifiedKey: String { return "ModifiedColor" }
30 | open var addedKey: String { return "AddedColor" }
31 | open var extraKey: String { return "ExtraColor" }
32 | open var recentKey: String { return "RecentColor" }
33 | open var formatKey: String { return "FormatColor" }
34 | open var inferKey: String { return "InferColor" }
35 |
36 | open var showHeadKey: String { return "ShowHead" }
37 | open var recentDaysKey: String { return "RecentDays" }
38 | open var formatIndentKey: String { return "FormatIndent" }
39 |
40 | open lazy var wellKeys: [NSColorWell: String] = [
41 | self.popoverColorWell: self.popoverKey,
42 | self.deletedColorWell: self.deletedKey,
43 | self.modifiedColorWell: self.modifiedKey,
44 | self.addedColorWell: self.addedKey,
45 | self.extraColorWell: self.extraKey,
46 | self.recentColorWell: self.recentKey,
47 | self.formatColorWell: self.formatKey,
48 | self.inferColorWell: self.inferKey,
49 | ]
50 |
51 | open override func awakeFromNib() {
52 | for (colorWell, key) in wellKeys {
53 | setup(colorWell, key: key)
54 | }
55 | if let existing = defaults.value(forKey: recentDaysKey) {
56 | recentDaysField?.stringValue = existing as! String
57 | } else if recentDaysField != nil {
58 | defaults.set(recentDaysField.stringValue, forKey: recentDaysKey)
59 | }
60 | if let existing = defaults.value(forKey: formatIndentKey) {
61 | formatIndentField?.stringValue = existing as! String
62 | } else if formatIndentField != nil {
63 | defaults.set(formatIndentField.stringValue, forKey: formatIndentKey)
64 | }
65 | defaults.synchronize()
66 | }
67 |
68 | open func setup(_ colorWell: NSColorWell?, key: String) {
69 | if let existing = defaults.value(forKey: key) {
70 | colorWell?.color = NSColor(string: existing as! String)
71 | } else if colorWell != nil {
72 | defaults.set(colorWell!.color.stringRepresentation, forKey: key)
73 | }
74 | }
75 |
76 | @IBAction func colorChanged(sender: NSColorWell) {
77 | if let key = wellKeys[sender] {
78 | defaults.set(sender.color.stringRepresentation, forKey: key)
79 | }
80 | }
81 |
82 | @IBAction func reset(sender: NSButton) {
83 | for (_, key) in wellKeys {
84 | defaults.removeObject(forKey: key)
85 | }
86 | popoverColorWell?.color = popoverColor
87 | deletedColorWell?.color = deletedColor
88 | modifiedColorWell?.color = modifiedColor
89 | addedColorWell?.color = addedColor
90 | extraColorWell?.color = extraColor
91 | recentColorWell?.color = recentColor
92 | formatColorWell?.color = formatColor
93 | inferColorWell?.color = inferColor
94 | awakeFromNib()
95 | }
96 |
97 | open func defaultColor(for key: String, default value: String) -> NSColor {
98 | return NSColor(string: defaults.value(forKey: key) as? String ?? value)
99 | }
100 |
101 | open var popoverColor: NSColor {
102 | return defaultColor(for: popoverKey, default: "1 0.914 0.662 1")
103 | }
104 |
105 | open var deletedColor: NSColor {
106 | return defaultColor(for: deletedKey, default: "1 0.584 0.571 1")
107 | }
108 |
109 | open var modifiedColor: NSColor {
110 | return defaultColor(for: modifiedKey, default: "1 0.576 0 1")
111 | }
112 |
113 | open var addedColor: NSColor {
114 | return defaultColor(for: addedKey, default: "0.253 0.659 0.694 1")
115 | }
116 |
117 | open var extraColor: NSColor {
118 | return defaultColor(for: extraKey, default: "0.5 0 0 1")
119 | }
120 |
121 | open var recentColor: NSColor {
122 | return defaultColor(for: recentKey, default: "0.5 1.0 0.5 1")
123 | }
124 |
125 | open var formatColor: NSColor {
126 | return defaultColor(for: formatKey, default: "0.129 0.313 1 1")
127 | }
128 |
129 | open var inferColor: NSColor {
130 | return defaultColor(for: inferKey, default: "0.646293 0.919667 1 1")
131 | }
132 |
133 | open var showHead: Bool {
134 | return defaults.bool(forKey: showHeadKey)
135 | }
136 |
137 | @IBAction open func showHeadChanged(sender: NSButton) {
138 | defaults.set(sender.state == .on, forKey: showHeadKey)
139 | }
140 |
141 | @IBAction open func recentChanged(sender: NSTextField) {
142 | defaults.setValue(sender.stringValue, forKey: recentDaysKey)
143 | }
144 |
145 | @IBAction open func indentChanged(sender: NSTextField) {
146 | defaults.setValue(sender.stringValue, forKey: formatIndentKey)
147 | }
148 |
149 | }
150 |
--------------------------------------------------------------------------------
/LNProvider/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSApplicationCategoryType
24 | public.app-category.developer-tools
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSHumanReadableCopyright
28 | Copyright © 2017 John Holdsworth. All rights reserved.
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 | LSUIElement
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/LNProvider/LNProvider-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "LNExtensionClient.h"
6 |
7 |
--------------------------------------------------------------------------------
/LNProvider/LNProvider.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LNProviderTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/LNProviderTests/LNProviderTests-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "LNExtensionClient.h"
6 |
7 | #import "DiffMatchPatch.h"
8 | #import "DMDiff.h"
9 |
--------------------------------------------------------------------------------
/LNProviderTests/LNProviderTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LNProviderTests.swift
3 | // LNProviderTests
4 | //
5 | // Created by John Holdsworth on 31/03/2017.
6 | // Copyright © 2017 John Holdsworth. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import LNProvider
11 |
12 | class LNProviderTests: XCTestCase {
13 |
14 | var service: LNExtensionClient!
15 |
16 | override func setUp() {
17 | super.setUp()
18 | // Put setup code here. This method is called before the invocation of each test method in the class.
19 | // service = LNExtensionClient(serviceName: "com.johnholdsworth.GitBlameRelay", delegate: nil)
20 | }
21 |
22 | override func tearDown() {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | super.tearDown()
25 | }
26 |
27 | func testBlame() {
28 | if let service = service {
29 | // This is an example of a functional test case.
30 | // Use XCTAssert and related functions to verify your tests produce the correct results.
31 | service.requestHighlights(forFile: #file)
32 | RunLoop.main.run(until: Date(timeIntervalSinceNow: 2.0))
33 | if let highlights = service[(#file)] {
34 | let element = highlights[(#line)]
35 | XCTAssertNotNil(element, "Got blame")
36 | NSLog("element: \(String(describing: element))")
37 | } else {
38 | XCTFail("No highlights")
39 | }
40 | }
41 | }
42 |
43 | func testSerializing() {
44 | let reference = LNFileHighlights()
45 | for i in stride(from: 1, to: 100, by: 10) {
46 | let element = LNHighlightElement()
47 | element.start = i
48 | element.color = NSColor(string: ".1 .3 .3 .4")
49 | element.text = "\(i)"
50 | element.range = "\(i) \(i + 1)"
51 | reference[i] = element
52 | for j in 1 ... 9 {
53 | reference[i + j] = element
54 | }
55 | }
56 |
57 | let highlights = LNFileHighlights(data: reference.jsonData(), service:"none")!
58 | XCTAssertTrue(highlights[1] == reference[1], "basic")
59 | XCTAssertTrue(highlights[1] != reference[11], "other")
60 | XCTAssertTrue(highlights[1] === highlights[2], "alias")
61 | }
62 |
63 | func testDiff() {
64 | let path = Bundle(for: type(of: self)).path(forResource: "example_diff", ofType: "txt")
65 | let sequence = FileGenerator(path: path!)!.lineSequence
66 | let highlights = DiffProcessor().generateHighlights(sequence: sequence, defaults: DefaultManager())
67 | print(String(data: highlights.jsonData(), encoding: .utf8)!)
68 | highlights.foreachHighlightRange {
69 | (range, element) in
70 | print(range, element)
71 | }
72 | }
73 |
74 | func testFormat() {
75 | FormatImpl(connection: nil)?.requestHighlights(forFile: #file, callback: {
76 | json, _ in
77 | if let json = json {
78 | print(String(data: json, encoding: .utf8)!)
79 | }
80 | })
81 | }
82 |
83 | func testAttributed() {
84 | let element = LNHighlightElement()
85 | let string = NSMutableAttributedString(string: "hello world")
86 | string.addAttributes([NSFontAttributeName: NSFont(name: "Arial", size: 10)!], range: NSMakeRange(0, 4))
87 | element.setAttributedText(string)
88 | print("\(String(describing: element.attributedText()))")
89 | }
90 |
91 | func testPerformanceExample() {
92 | // This is an example of a performance test case.
93 | measure {
94 | // Put the code you want to measure the time of here.
95 | }
96 | }
97 |
98 | }
99 |
--------------------------------------------------------------------------------
/LNProviderTests/example_diff.txt:
--------------------------------------------------------------------------------
1 | diff --git a/LNXcodeSupport/LNFileHighlights.mm b/LNXcodeSupport/LNFileHighlights.mm
2 | index 894020a..e75cc24 100644
3 | --- a/LNXcodeSupport/LNFileHighlights.mm
4 | +++ b/LNXcodeSupport/LNFileHighlights.mm
5 | @@ -7,6 +7,7 @@
6 | //
7 |
8 | #import "LNFileHighlights.h"
9 | +#import "NSColor+NSString.h"
10 | #import