├── .gitignore
├── README.md
├── Reveal-In-GitHub.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
└── Reveal-In-GitHub
├── Info.plist
├── RIGConfig.h
├── RIGConfig.m
├── RIGConfigCellView.h
├── RIGConfigCellView.m
├── RIGConfigCellsView.h
├── RIGConfigCellsView.m
├── RIGGitRepo.h
├── RIGGitRepo.m
├── RIGPlugin.h
├── RIGPlugin.m
├── RIGSetting.h
├── RIGSetting.m
├── RIGSettingWindowController.h
├── RIGSettingWindowController.m
├── RIGSettingWindowController.xib
├── RIGUtils.h
└── RIGUtils.m
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata
19 |
20 | ## Other
21 | *.xccheckout
22 | *.moved-aside
23 | *.xcuserstate
24 |
25 | ## Obj-C/Swift specific
26 | *.hmap
27 | *.ipa
28 |
29 | # CocoaPods
30 | Pods/
31 | Podfile.lock
32 |
33 | *.gcda
34 | *.gcno
35 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Reveal-In-GitHub
2 |
3 | An Xcode plugin designed for seamless navigation to key GitHub functionalities within your current repository. With just a click, effortlessly access GitHub History, Blame, Pull Requests, Issues, and Notifications, all in a matter of seconds.
4 |
5 | 
6 |
7 | My company work on GitHub. I open the GitHub a lot. Sometimes, I am editing on Xcode and do not understand some code, so I go to GitHub to blame them. Sometimes, find the latest commits about a file to help me figure out how the code evolves. So I wonder is there a tool to help me quick open GitHub from Xcode. So I write this plugin. When you editing some source file on Xcode, it's easy to know which GitHub repo you are working on and know which file you are editing. So it makes sense to quickly jump to the file on GitHub, quickly jump to blame the current editing line on GitHub, quickly jump to the issues or prs of the current repo you are working on Xcode.
8 |
9 | ## Menu Items
10 |
11 |
12 |
13 | It has six menu items:
14 |
15 | Menu Title | Shortcut | GitHub URL Pattern (When I'm editing LZAlbumManager.m Line 40)
16 | ----------------|-----------------------|----------------------------------
17 | Setting |⌃⇧⌘S |
18 | Repo |⌃⇧⌘R | https://github.com/lzwjava/LZAlbum
19 | Issues |⌃⇧⌘I | https://github.com/lzwjava/LZAlbum/issues
20 | PRs |⌃⇧⌘P | https://github.com/lzwjava/LZAlbum/pulls
21 | Quick File |⌃⇧⌘Q | https://github.com/lzwjava/LZAlbum/blob/fd7224/LZAlbum/manager/LZAlbumManager.m#L40
22 | List History |⌃⇧⌘L | https://github.com/lzwjava/LZAlbum/commits/fd7224/LZAlbum/manager/LZAlbumManager.m
23 | Blame |⌃⇧⌘B | https://github.com/lzwjava/LZAlbum/blame/fd7224/LZAlbum/manager/LZAlbumManager.m#L40
24 | Notifications |⌃⇧⌘N | https://github.com/leancloud/LZAlbum/notifications?all=1
25 |
26 | The shortcuts are carefully designed. They will not confict to Xcode default shortcuts. The shortcut pattern is ⌃⇧⌘ (Ctrl+Shift+Command), plus First Character of the menu title.
27 |
28 | ## Customize
29 |
30 | Sometimes, you may want to quickly jump to Wiki. Here is the way, open the setting:
31 |
32 |
33 |
34 | For example,
35 |
36 | Quick file, the pattern and the actual url:
37 |
38 | ```
39 | {git_remote_url} /blob/{commit}/ {file_path} #{selection}
40 | https://github.com/lzwjava/LZAlbum/blob/fd7224/LZAlbum/manager/LZAlbumManager.m#L40-L43
41 | ```
42 |
43 | The {commit} is the latest commit hash of current branch. It's better then use branch. Because branch's HEAD may be changed. So the code in #L40-L43 may also be changed.
44 |
45 | So if you want to add a shortcut to current repo's wiki, just add a menu item and set the pattern to ` {git_remote_url}/wiki`.
46 |
47 | In settings, `Clear Default Repos` say if you have multiple git remotes, when first time to trigger, it will ask you to choose one of them:
48 |
49 |
50 |
51 | Then the plugin remembers which you choose. So when you trigger the menu again, will open that remote repo as the default. The button `Clear Default Repos` will clear this setting, will ask you to select again.
52 |
53 | ## Install
54 |
55 | Recomend install with [Alcatraz](http://alcatraz.io/),
56 |
57 | 
58 |
59 | Or
60 |
61 | 1. Clone this repo.
62 | 2. Open `Reveal-In-GitHub.xcodeproj`, and build it.
63 | 3. Reveal-In-GitHub.xcplugin should locate at `~/Library/Application Support/Developer/Shared/Xcode/Plug-ins`
64 | 4. Restart Xcode
65 | 5. Open any GitHub Project and press ⌃⇧⌘B (Ctrl+Shift+Command+B) to blame the code.
66 |
67 | ## 安装
68 |
69 | 推荐使用 [Alcatraz](http://alcatraz.io/),可参考巧神的一遍[博客](http://blog.devtang.com/blog/2014/03/05/use-alcatraz-to-manage-xcode-plugins/)。安装好后,按照上图搜索 `Reveal In GitHub`,点击 `Install` 即可。
70 |
71 | 如果不使用这个工具的话,那么也只需三步:
72 |
73 | * 克隆该项目到本地。
74 | * 打开 xcodeproj,点击 Build 构建。这会在目录`~/Library/Application Support/Developer/Shared/Xcode/Plug-ins` 生成 Reveal-In-GitHub.xcplugin 文件。
75 | * 重启 Xcode,打开任意一个放在 GitHub 上的项目。按下 `Ctrl+Shift+Command+B` 即可。
76 |
77 | ## Credit
78 |
79 | When at the course of developing it, find another plugin [ShowInGitHub](https://github.com/larsxschneider/ShowInGitHub) do something similar. I learn some techique from it. Thanks for that.
80 |
81 | ## License
82 |
83 | MIT
84 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 706F254E1BE7C76E00CA15B4 /* RIGConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 706F253D1BE7C76E00CA15B4 /* RIGConfig.m */; };
11 | 706F254F1BE7C76E00CA15B4 /* RIGConfigCellsView.m in Sources */ = {isa = PBXBuildFile; fileRef = 706F253F1BE7C76E00CA15B4 /* RIGConfigCellsView.m */; };
12 | 706F25501BE7C76E00CA15B4 /* RIGConfigCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = 706F25411BE7C76E00CA15B4 /* RIGConfigCellView.m */; };
13 | 706F25511BE7C76E00CA15B4 /* RIGGitRepo.m in Sources */ = {isa = PBXBuildFile; fileRef = 706F25431BE7C76E00CA15B4 /* RIGGitRepo.m */; };
14 | 706F25521BE7C76E00CA15B4 /* RIGPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 706F25451BE7C76E00CA15B4 /* RIGPlugin.m */; };
15 | 706F25531BE7C76E00CA15B4 /* RIGSetting.m in Sources */ = {isa = PBXBuildFile; fileRef = 706F25471BE7C76E00CA15B4 /* RIGSetting.m */; };
16 | 706F25541BE7C76E00CA15B4 /* RIGSettingWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 706F25491BE7C76E00CA15B4 /* RIGSettingWindowController.m */; };
17 | 706F25551BE7C76E00CA15B4 /* RIGSettingWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 706F254A1BE7C76E00CA15B4 /* RIGSettingWindowController.xib */; };
18 | 706F25561BE7C76E00CA15B4 /* RIGUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 706F254C1BE7C76E00CA15B4 /* RIGUtils.m */; };
19 | 70DE90B11BD932D300E29A15 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70DE90B01BD932D300E29A15 /* Cocoa.framework */; };
20 | 70DE90B31BD932D800E29A15 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70DE90B21BD932D800E29A15 /* Foundation.framework */; };
21 | /* End PBXBuildFile section */
22 |
23 | /* Begin PBXFileReference section */
24 | 705EEF501BD80FB900A29083 /* Reveal-In-GitHub.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Reveal-In-GitHub.xcplugin"; sourceTree = BUILT_PRODUCTS_DIR; };
25 | 706F253B1BE7C76E00CA15B4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
26 | 706F253C1BE7C76E00CA15B4 /* RIGConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RIGConfig.h; sourceTree = ""; };
27 | 706F253D1BE7C76E00CA15B4 /* RIGConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RIGConfig.m; sourceTree = ""; };
28 | 706F253E1BE7C76E00CA15B4 /* RIGConfigCellsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RIGConfigCellsView.h; sourceTree = ""; };
29 | 706F253F1BE7C76E00CA15B4 /* RIGConfigCellsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RIGConfigCellsView.m; sourceTree = ""; };
30 | 706F25401BE7C76E00CA15B4 /* RIGConfigCellView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RIGConfigCellView.h; sourceTree = ""; };
31 | 706F25411BE7C76E00CA15B4 /* RIGConfigCellView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RIGConfigCellView.m; sourceTree = ""; };
32 | 706F25421BE7C76E00CA15B4 /* RIGGitRepo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RIGGitRepo.h; sourceTree = ""; };
33 | 706F25431BE7C76E00CA15B4 /* RIGGitRepo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RIGGitRepo.m; sourceTree = ""; };
34 | 706F25441BE7C76E00CA15B4 /* RIGPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RIGPlugin.h; sourceTree = ""; };
35 | 706F25451BE7C76E00CA15B4 /* RIGPlugin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RIGPlugin.m; sourceTree = ""; };
36 | 706F25461BE7C76E00CA15B4 /* RIGSetting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RIGSetting.h; sourceTree = ""; };
37 | 706F25471BE7C76E00CA15B4 /* RIGSetting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RIGSetting.m; sourceTree = ""; };
38 | 706F25481BE7C76E00CA15B4 /* RIGSettingWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RIGSettingWindowController.h; sourceTree = ""; };
39 | 706F25491BE7C76E00CA15B4 /* RIGSettingWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RIGSettingWindowController.m; sourceTree = ""; };
40 | 706F254A1BE7C76E00CA15B4 /* RIGSettingWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RIGSettingWindowController.xib; sourceTree = ""; };
41 | 706F254B1BE7C76E00CA15B4 /* RIGUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RIGUtils.h; sourceTree = ""; };
42 | 706F254C1BE7C76E00CA15B4 /* RIGUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RIGUtils.m; sourceTree = ""; };
43 | 70DE90B01BD932D300E29A15 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
44 | 70DE90B21BD932D800E29A15 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 705EEF4D1BD80FB900A29083 /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | 70DE90B31BD932D800E29A15 /* Foundation.framework in Frameworks */,
53 | 70DE90B11BD932D300E29A15 /* Cocoa.framework in Frameworks */,
54 | );
55 | runOnlyForDeploymentPostprocessing = 0;
56 | };
57 | /* End PBXFrameworksBuildPhase section */
58 |
59 | /* Begin PBXGroup section */
60 | 705EEF471BD80FB900A29083 = {
61 | isa = PBXGroup;
62 | children = (
63 | 70DE90B21BD932D800E29A15 /* Foundation.framework */,
64 | 70DE90B01BD932D300E29A15 /* Cocoa.framework */,
65 | 706F25391BE7C76E00CA15B4 /* Reveal-In-GitHub */,
66 | 705EEF511BD80FB900A29083 /* Products */,
67 | );
68 | sourceTree = "";
69 | };
70 | 705EEF511BD80FB900A29083 /* Products */ = {
71 | isa = PBXGroup;
72 | children = (
73 | 705EEF501BD80FB900A29083 /* Reveal-In-GitHub.xcplugin */,
74 | );
75 | name = Products;
76 | sourceTree = "";
77 | };
78 | 706F25391BE7C76E00CA15B4 /* Reveal-In-GitHub */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 706F253B1BE7C76E00CA15B4 /* Info.plist */,
82 | 706F253C1BE7C76E00CA15B4 /* RIGConfig.h */,
83 | 706F253D1BE7C76E00CA15B4 /* RIGConfig.m */,
84 | 706F253E1BE7C76E00CA15B4 /* RIGConfigCellsView.h */,
85 | 706F253F1BE7C76E00CA15B4 /* RIGConfigCellsView.m */,
86 | 706F25401BE7C76E00CA15B4 /* RIGConfigCellView.h */,
87 | 706F25411BE7C76E00CA15B4 /* RIGConfigCellView.m */,
88 | 706F25421BE7C76E00CA15B4 /* RIGGitRepo.h */,
89 | 706F25431BE7C76E00CA15B4 /* RIGGitRepo.m */,
90 | 706F25441BE7C76E00CA15B4 /* RIGPlugin.h */,
91 | 706F25451BE7C76E00CA15B4 /* RIGPlugin.m */,
92 | 706F25461BE7C76E00CA15B4 /* RIGSetting.h */,
93 | 706F25471BE7C76E00CA15B4 /* RIGSetting.m */,
94 | 706F25481BE7C76E00CA15B4 /* RIGSettingWindowController.h */,
95 | 706F25491BE7C76E00CA15B4 /* RIGSettingWindowController.m */,
96 | 706F254A1BE7C76E00CA15B4 /* RIGSettingWindowController.xib */,
97 | 706F254B1BE7C76E00CA15B4 /* RIGUtils.h */,
98 | 706F254C1BE7C76E00CA15B4 /* RIGUtils.m */,
99 | );
100 | path = "Reveal-In-GitHub";
101 | sourceTree = "";
102 | };
103 | /* End PBXGroup section */
104 |
105 | /* Begin PBXNativeTarget section */
106 | 705EEF4F1BD80FB900A29083 /* Reveal-In-GitHub */ = {
107 | isa = PBXNativeTarget;
108 | buildConfigurationList = 705EEF561BD80FB900A29083 /* Build configuration list for PBXNativeTarget "Reveal-In-GitHub" */;
109 | buildPhases = (
110 | 705EEF4C1BD80FB900A29083 /* Sources */,
111 | 705EEF4D1BD80FB900A29083 /* Frameworks */,
112 | 705EEF4E1BD80FB900A29083 /* Resources */,
113 | );
114 | buildRules = (
115 | );
116 | dependencies = (
117 | );
118 | name = "Reveal-In-GitHub";
119 | productName = "Reveal-In-Github";
120 | productReference = 705EEF501BD80FB900A29083 /* Reveal-In-GitHub.xcplugin */;
121 | productType = "com.apple.product-type.bundle";
122 | };
123 | /* End PBXNativeTarget section */
124 |
125 | /* Begin PBXProject section */
126 | 705EEF481BD80FB900A29083 /* Project object */ = {
127 | isa = PBXProject;
128 | attributes = {
129 | LastUpgradeCheck = 0700;
130 | ORGANIZATIONNAME = lzwjava;
131 | TargetAttributes = {
132 | 705EEF4F1BD80FB900A29083 = {
133 | CreatedOnToolsVersion = 7.0;
134 | };
135 | };
136 | };
137 | buildConfigurationList = 705EEF4B1BD80FB900A29083 /* Build configuration list for PBXProject "Reveal-In-GitHub" */;
138 | compatibilityVersion = "Xcode 3.2";
139 | developmentRegion = English;
140 | hasScannedForEncodings = 0;
141 | knownRegions = (
142 | en,
143 | );
144 | mainGroup = 705EEF471BD80FB900A29083;
145 | productRefGroup = 705EEF511BD80FB900A29083 /* Products */;
146 | projectDirPath = "";
147 | projectRoot = "";
148 | targets = (
149 | 705EEF4F1BD80FB900A29083 /* Reveal-In-GitHub */,
150 | );
151 | };
152 | /* End PBXProject section */
153 |
154 | /* Begin PBXResourcesBuildPhase section */
155 | 705EEF4E1BD80FB900A29083 /* Resources */ = {
156 | isa = PBXResourcesBuildPhase;
157 | buildActionMask = 2147483647;
158 | files = (
159 | 706F25551BE7C76E00CA15B4 /* RIGSettingWindowController.xib in Resources */,
160 | );
161 | runOnlyForDeploymentPostprocessing = 0;
162 | };
163 | /* End PBXResourcesBuildPhase section */
164 |
165 | /* Begin PBXSourcesBuildPhase section */
166 | 705EEF4C1BD80FB900A29083 /* Sources */ = {
167 | isa = PBXSourcesBuildPhase;
168 | buildActionMask = 2147483647;
169 | files = (
170 | 706F25521BE7C76E00CA15B4 /* RIGPlugin.m in Sources */,
171 | 706F25541BE7C76E00CA15B4 /* RIGSettingWindowController.m in Sources */,
172 | 706F25531BE7C76E00CA15B4 /* RIGSetting.m in Sources */,
173 | 706F254F1BE7C76E00CA15B4 /* RIGConfigCellsView.m in Sources */,
174 | 706F25561BE7C76E00CA15B4 /* RIGUtils.m in Sources */,
175 | 706F25501BE7C76E00CA15B4 /* RIGConfigCellView.m in Sources */,
176 | 706F254E1BE7C76E00CA15B4 /* RIGConfig.m in Sources */,
177 | 706F25511BE7C76E00CA15B4 /* RIGGitRepo.m in Sources */,
178 | );
179 | runOnlyForDeploymentPostprocessing = 0;
180 | };
181 | /* End PBXSourcesBuildPhase section */
182 |
183 | /* Begin XCBuildConfiguration section */
184 | 705EEF541BD80FB900A29083 /* Debug */ = {
185 | isa = XCBuildConfiguration;
186 | buildSettings = {
187 | ALWAYS_SEARCH_USER_PATHS = NO;
188 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
189 | CLANG_CXX_LIBRARY = "libc++";
190 | CLANG_ENABLE_MODULES = YES;
191 | CLANG_ENABLE_OBJC_ARC = YES;
192 | CLANG_WARN_BOOL_CONVERSION = YES;
193 | CLANG_WARN_CONSTANT_CONVERSION = YES;
194 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
195 | CLANG_WARN_EMPTY_BODY = YES;
196 | CLANG_WARN_ENUM_CONVERSION = YES;
197 | CLANG_WARN_INT_CONVERSION = YES;
198 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
199 | CLANG_WARN_UNREACHABLE_CODE = YES;
200 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
201 | COPY_PHASE_STRIP = NO;
202 | DEBUG_INFORMATION_FORMAT = dwarf;
203 | ENABLE_STRICT_OBJC_MSGSEND = YES;
204 | ENABLE_TESTABILITY = YES;
205 | GCC_C_LANGUAGE_STANDARD = gnu99;
206 | GCC_DYNAMIC_NO_PIC = NO;
207 | GCC_NO_COMMON_BLOCKS = YES;
208 | GCC_OPTIMIZATION_LEVEL = 0;
209 | GCC_PREPROCESSOR_DEFINITIONS = (
210 | "DEBUG=1",
211 | "$(inherited)",
212 | );
213 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
214 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
215 | GCC_WARN_UNDECLARED_SELECTOR = YES;
216 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
217 | GCC_WARN_UNUSED_FUNCTION = YES;
218 | GCC_WARN_UNUSED_VARIABLE = YES;
219 | MACOSX_DEPLOYMENT_TARGET = 10.10;
220 | MTL_ENABLE_DEBUG_INFO = YES;
221 | ONLY_ACTIVE_ARCH = YES;
222 | SDKROOT = macosx;
223 | };
224 | name = Debug;
225 | };
226 | 705EEF551BD80FB900A29083 /* Release */ = {
227 | isa = XCBuildConfiguration;
228 | buildSettings = {
229 | ALWAYS_SEARCH_USER_PATHS = NO;
230 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
231 | CLANG_CXX_LIBRARY = "libc++";
232 | CLANG_ENABLE_MODULES = YES;
233 | CLANG_ENABLE_OBJC_ARC = YES;
234 | CLANG_WARN_BOOL_CONVERSION = YES;
235 | CLANG_WARN_CONSTANT_CONVERSION = YES;
236 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
237 | CLANG_WARN_EMPTY_BODY = YES;
238 | CLANG_WARN_ENUM_CONVERSION = YES;
239 | CLANG_WARN_INT_CONVERSION = YES;
240 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
241 | CLANG_WARN_UNREACHABLE_CODE = YES;
242 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
243 | COPY_PHASE_STRIP = NO;
244 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
245 | ENABLE_NS_ASSERTIONS = NO;
246 | ENABLE_STRICT_OBJC_MSGSEND = YES;
247 | GCC_C_LANGUAGE_STANDARD = gnu99;
248 | GCC_NO_COMMON_BLOCKS = YES;
249 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
250 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
251 | GCC_WARN_UNDECLARED_SELECTOR = YES;
252 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
253 | GCC_WARN_UNUSED_FUNCTION = YES;
254 | GCC_WARN_UNUSED_VARIABLE = YES;
255 | MACOSX_DEPLOYMENT_TARGET = 10.10;
256 | MTL_ENABLE_DEBUG_INFO = NO;
257 | SDKROOT = macosx;
258 | };
259 | name = Release;
260 | };
261 | 705EEF571BD80FB900A29083 /* Debug */ = {
262 | isa = XCBuildConfiguration;
263 | buildSettings = {
264 | COMBINE_HIDPI_IMAGES = YES;
265 | DEPLOYMENT_LOCATION = YES;
266 | DSTROOT = "";
267 | GCC_PREPROCESSOR_DEFINITIONS = (
268 | "DEBUG=1",
269 | "$(inherited)",
270 | );
271 | HEADER_SEARCH_PATHS = "";
272 | INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist";
273 | INSTALL_PATH = "~/Library/Application Support/Developer/Shared/Xcode/Plug-ins";
274 | PRODUCT_BUNDLE_IDENTIFIER = "com.lzwjava.Reveal-In-GitHub";
275 | PRODUCT_NAME = "$(TARGET_NAME)";
276 | SKIP_INSTALL = NO;
277 | WRAPPER_EXTENSION = xcplugin;
278 | };
279 | name = Debug;
280 | };
281 | 705EEF581BD80FB900A29083 /* Release */ = {
282 | isa = XCBuildConfiguration;
283 | buildSettings = {
284 | COMBINE_HIDPI_IMAGES = YES;
285 | DEPLOYMENT_LOCATION = YES;
286 | DSTROOT = "";
287 | HEADER_SEARCH_PATHS = "";
288 | INFOPLIST_FILE = "$(TARGET_NAME)/Info.plist";
289 | INSTALL_PATH = "~/Library/Application Support/Developer/Shared/Xcode/Plug-ins";
290 | PRODUCT_BUNDLE_IDENTIFIER = "com.lzwjava.Reveal-In-GitHub";
291 | PRODUCT_NAME = "$(TARGET_NAME)";
292 | SKIP_INSTALL = NO;
293 | WRAPPER_EXTENSION = xcplugin;
294 | };
295 | name = Release;
296 | };
297 | /* End XCBuildConfiguration section */
298 |
299 | /* Begin XCConfigurationList section */
300 | 705EEF4B1BD80FB900A29083 /* Build configuration list for PBXProject "Reveal-In-GitHub" */ = {
301 | isa = XCConfigurationList;
302 | buildConfigurations = (
303 | 705EEF541BD80FB900A29083 /* Debug */,
304 | 705EEF551BD80FB900A29083 /* Release */,
305 | );
306 | defaultConfigurationIsVisible = 0;
307 | defaultConfigurationName = Release;
308 | };
309 | 705EEF561BD80FB900A29083 /* Build configuration list for PBXNativeTarget "Reveal-In-GitHub" */ = {
310 | isa = XCConfigurationList;
311 | buildConfigurations = (
312 | 705EEF571BD80FB900A29083 /* Debug */,
313 | 705EEF581BD80FB900A29083 /* Release */,
314 | );
315 | defaultConfigurationIsVisible = 0;
316 | defaultConfigurationName = Release;
317 | };
318 | /* End XCConfigurationList section */
319 | };
320 | rootObject = 705EEF481BD80FB900A29083 /* Project object */;
321 | }
322 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/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 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | DVTPlugInCompatibilityUUIDs
24 |
25 | 63FC1C47-140D-42B0-BB4D-A10B2D225574
26 | 37B30044-3B14-46BA-ABAA-F01000C27B63
27 | 640F884E-CE55-4B40-87C0-8869546CAB7A
28 | A2E4D43F-41F4-4FB9-BB94-7177011C9AED
29 | AD68E85B-441B-4301-B564-A45E4919A6AD
30 | C4A681B0-4A26-480E-93EC-1218098B9AA0
31 | FEC992CC-CA4A-4CFD-8881-77300FCB848A
32 | 992275C1-432A-4CF7-B659-D84ED6D42D3F
33 | A16FF353-8441-459E-A50C-B071F53F51B7
34 | 9F75337B-21B4-4ADC-B558-F9CADF7073A7
35 | 992275C1-432A-4CF7-B659-D84ED6D42D3F
36 | 8DC44374-2B35-4C57-A6FE-2AD66A36AAD9
37 | E969541F-E6F9-4D25-8158-72DC3545A6C6
38 | AABB7188-E14E-4433-AD3B-5CD791EAD9A3
39 | 7FDF5C7A-131F-4ABB-9EDC-8C5F8F0B8A90
40 | 0420B86A-AA43-4792-9ED0-6FE0F2B16A13
41 | CC0D0F4F-05B3-431A-8F33-F84AFCB2C651
42 | 7265231C-39B4-402C-89E1-16167C4CC990
43 | 0420B86A-AA43-4792-9ED0-6FE0F2B16A13
44 | F41BD31E-2683-44B8-AE7F-5F09E919790E
45 | ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C
46 |
47 | NSHumanReadableCopyright
48 | Copyright © 2015年 lzwjava. All rights reserved.
49 | NSPrincipalClass
50 |
51 | XC4Compatible
52 |
53 | XC5Compatible
54 |
55 | XCGCReady
56 |
57 | XCPluginHasUI
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGConfig.h:
--------------------------------------------------------------------------------
1 | //
2 | // RIGConfig.h
3 | // Reveal-In-GitHub
4 | //
5 | // Created by lzw on 15/10/28.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RIGConfig : NSObject
12 |
13 | @property (nonatomic, strong) NSString *menuTitle;
14 | @property (nonatomic, strong) NSString *lastKey;
15 | @property (nonatomic, strong) NSString *pattern;
16 |
17 | - (instancetype)initWithDictionary:(NSDictionary *)dict;
18 | - (NSDictionary *)dictionary;
19 |
20 | + (instancetype)configWithMenuTitle:(NSString *)menuTitle lastKey:(NSString *)lastKey pattern:(NSString *)pattern;
21 |
22 | - (BOOL)isValid;
23 |
24 | - (void)removeNil;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGConfig.m:
--------------------------------------------------------------------------------
1 | //
2 | // RIGConfig.m
3 | // Reveal-In-GitHub
4 | //
5 | // Created by lzw on 15/10/28.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import "RIGConfig.h"
10 |
11 | #define SEL_TO_STRING(sel) NSStringFromSelector(@selector(sel))
12 |
13 | @implementation RIGConfig
14 |
15 | - (instancetype)initWithDictionary:(NSDictionary *)dict {
16 | self = [super init];
17 | if (self) {
18 | _menuTitle = dict[SEL_TO_STRING(menuTitle)];
19 | _lastKey = dict[SEL_TO_STRING(lastKey)];
20 | _pattern = dict[SEL_TO_STRING(pattern)];
21 | }
22 | return self;
23 | }
24 |
25 | - (NSDictionary *)dictionary {
26 | NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
27 | if (self.menuTitle.length > 0) {
28 | [dict setObject:self.menuTitle forKey:SEL_TO_STRING(menuTitle)];
29 | }
30 | if (self.lastKey.length > 0) {
31 | [dict setObject:self.lastKey forKey:SEL_TO_STRING(lastKey)];
32 | }
33 | if (self.pattern.length > 0) {
34 | [dict setObject:self.pattern forKey:SEL_TO_STRING(pattern)];
35 | }
36 | return dict;
37 | }
38 |
39 | + (instancetype)configWithMenuTitle:(NSString *)menuTitle lastKey:(NSString *)lastKey pattern:(NSString *)pattern {
40 | RIGConfig *config = [[self alloc] init];
41 | config.menuTitle = menuTitle;
42 | config.lastKey = lastKey;
43 | config.pattern = pattern;
44 | return config;
45 | }
46 |
47 | - (BOOL)isValid {
48 | return self.menuTitle.length > 0 && self.pattern.length > 0;
49 | }
50 |
51 | - (NSString *)description {
52 | return [NSString stringWithFormat:@"RIGConfig
10 | #import "RIGConfig.h"
11 |
12 | @interface RIGConfigCellView : NSView
13 |
14 | @property (nonatomic, strong) NSTextField *menuTitleField;
15 | @property (nonatomic, strong) NSTextField *lastKeyField;
16 | @property (nonatomic, strong) NSTextField *patternField;
17 |
18 | @property (nonatomic, strong) RIGConfig *config;
19 |
20 | + (CGFloat)heightForCellView;
21 |
22 | - (void)reloadData;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGConfigCellView.m:
--------------------------------------------------------------------------------
1 | //
2 | // RIGConfigCellView.m
3 | // Reveal-In-GitHub
4 | //
5 | // Created by lzw on 15/10/28.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import "RIGConfigCellView.h"
10 |
11 | #define kHorizontalMargin 8
12 | #define kVerticalMargin 2
13 | #define kTextFieldHeight 25
14 | #define kMenuTitleWidth 100
15 | #define kLastKeyWidth 30
16 |
17 | @interface RIGConfigCellView()
18 |
19 | @end
20 |
21 | @implementation RIGConfigCellView
22 |
23 | - (instancetype)initWithFrame:(NSRect)frame
24 | {
25 | self = [super initWithFrame:frame];
26 | if (self) {
27 | // self.wantsLayer = YES;
28 | // self.layer.backgroundColor = [NSColor greenColor].CGColor;
29 | [self addSubview:self.menuTitleField];
30 | [self addSubview:self.lastKeyField];
31 | [self addSubview:self.patternField];
32 | }
33 | return self;
34 | }
35 |
36 | + (CGFloat)heightForCellView {
37 | return kVerticalMargin * 2 + kTextFieldHeight;
38 | }
39 |
40 | - (void)commonInitTextField:(NSTextField *)textField {
41 | textField.cell.wraps = NO;
42 | textField.cell.scrollable = YES;
43 | textField.delegate = self;
44 | // textField.font = [NSFont systemFontOfSize:16];
45 | }
46 |
47 | - (NSTextField *)menuTitleField {
48 | if (_menuTitleField == nil) {
49 | _menuTitleField = [[NSTextField alloc] initWithFrame:CGRectMake(kHorizontalMargin, kVerticalMargin, kMenuTitleWidth, kTextFieldHeight)];
50 | [self commonInitTextField:_menuTitleField];
51 | }
52 | return _menuTitleField;
53 | }
54 |
55 | - (NSTextField *)lastKeyField {
56 | if (_lastKeyField == nil) {
57 | _lastKeyField = [[NSTextField alloc] initWithFrame:CGRectMake(CGRectGetMaxX(self.menuTitleField.frame) + kHorizontalMargin, kVerticalMargin , kLastKeyWidth, kTextFieldHeight)];
58 | [self commonInitTextField:_lastKeyField];
59 | }
60 | return _lastKeyField;
61 | }
62 |
63 | - (NSTextField *)patternField {
64 | if (_patternField == nil) {
65 | CGFloat maxX = CGRectGetMaxX(self.lastKeyField.frame);
66 | _patternField = [[NSTextField alloc] initWithFrame:CGRectMake(maxX + kHorizontalMargin, kVerticalMargin , CGRectGetWidth(self.frame) - maxX - 2 * kHorizontalMargin, kTextFieldHeight)];
67 | [self commonInitTextField:_patternField];
68 | }
69 | return _patternField;
70 | }
71 |
72 | - (void)reloadData {
73 | [self.config removeNil];
74 | self.menuTitleField.stringValue = self.config.menuTitle;
75 | self.lastKeyField.stringValue = self.config.lastKey;
76 | self.patternField.stringValue = self.config.pattern;
77 | if (self.config) {
78 | NSDictionary* textFieldBindingOptions = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], NSValidatesImmediatelyBindingOption, [NSNumber numberWithBool:YES], NSContinuouslyUpdatesValueBindingOption,
79 | nil];
80 | [self.menuTitleField bind:@"value" toObject:self.config withKeyPath:@"menuTitle" options:textFieldBindingOptions];
81 | [self.lastKeyField bind:@"value" toObject:self.config withKeyPath:@"lastKey" options:textFieldBindingOptions];
82 | [self.patternField bind:@"value" toObject:self.config withKeyPath:@"pattern" options:textFieldBindingOptions];
83 | }
84 | }
85 |
86 | - (void)controlTextDidChange:(NSNotification *)notification {
87 | NSLog(@"Did change");
88 | }
89 |
90 | @end
91 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGConfigCellsView.h:
--------------------------------------------------------------------------------
1 | //
2 | // RIGConfigCellViews.h
3 | // Reveal-In-GitHub
4 | //
5 | // Created by lzw on 15/10/30.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RIGConfigCellsView : NSView
12 |
13 | @property (nonatomic, strong) NSArray *configs;
14 |
15 | + (CGFloat)heightForConfigs:(NSArray *)configs;
16 |
17 | - (void)reloadData;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGConfigCellsView.m:
--------------------------------------------------------------------------------
1 | //
2 | // RIGConfigCellViews.m
3 | // Reveal-In-GitHub
4 | //
5 | // Created by lzw on 15/10/30.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import "RIGConfigCellsView.h"
10 | #import "RIGConfigCellView.h"
11 |
12 | #define kVerticalMargin 5
13 |
14 | @interface RIGConfigCellsView()
15 |
16 | @property (nonatomic, strong) NSMutableArray *cellViews;
17 |
18 | @end
19 |
20 | @implementation RIGConfigCellsView
21 |
22 | - (instancetype)initWithFrame:(NSRect)frame
23 | {
24 | self = [super initWithFrame:frame];
25 | if (self) {
26 | _cellViews = [NSMutableArray array];
27 | }
28 | return self;
29 | }
30 |
31 | - (void)drawRect:(NSRect)dirtyRect {
32 | [super drawRect:dirtyRect];
33 |
34 | // Drawing code here.
35 | }
36 |
37 | + (CGFloat)heightForConfigs:(NSArray *)configs {
38 | return configs.count * [RIGConfigCellView heightForCellView] + (configs.count - 1) * kVerticalMargin;
39 | }
40 |
41 | - (void)reloadData {
42 | CGFloat w = CGRectGetWidth(self.frame);
43 | for (NSView *view in self.cellViews) {
44 | [view removeFromSuperview];
45 | }
46 | for (RIGConfig *config in self.configs) {
47 | NSInteger index = [self.configs indexOfObject:config];
48 | CGFloat subH = [RIGConfigCellView heightForCellView];
49 | CGFloat y = (self.configs.count - 1 - index) * (subH + kVerticalMargin);
50 | RIGConfigCellView *cellView = [[RIGConfigCellView alloc] initWithFrame:NSRectFromCGRect(CGRectMake(0, y, w, subH))];
51 | cellView.config = config;
52 | [cellView reloadData];
53 | [self.cellViews addObject:cellView];
54 | [self addSubview:cellView];
55 | }
56 | }
57 |
58 | @end
59 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGGitRepo.h:
--------------------------------------------------------------------------------
1 | //
2 | // RIGGitHelper.h
3 | // Reveal-In-GitHub
4 | //
5 | // Created by lzw on 15/10/31.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RIGGitRepo : NSObject
12 |
13 | @property (nonatomic, copy, readonly) NSString *localPath;
14 |
15 | - (instancetype)initWithDocumentURL:(NSURL *)documentURL;
16 |
17 | - (BOOL)isValid;
18 |
19 | - (NSString *)latestCommitHash;
20 | - (NSString *)filenameWithPathInCommit:(NSString *)commitHash;
21 |
22 | - (NSString *)remoteRepoUrl;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGGitRepo.m:
--------------------------------------------------------------------------------
1 | //
2 | // RIGGitHelper.m
3 | // Reveal-In-GitHub
4 | //
5 | // Created by lzw on 15/10/31.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import "RIGGitRepo.h"
10 | #import "RIGUtils.h"
11 | #import "RIGSetting.h"
12 |
13 | @interface RIGGitRepo()
14 |
15 | @property (nonatomic, strong) NSURL *documentURL;
16 | @property (nonatomic, strong) NSString *documentDirectoryPath;
17 | @property (nonatomic, copy) NSString *localPath;
18 |
19 | @end
20 |
21 | @implementation RIGGitRepo
22 |
23 | - (instancetype)initWithDocumentURL:(NSURL *)documentURL {
24 | if (self == [super init]) {
25 | _documentURL = documentURL;
26 | _documentDirectoryPath = [[documentURL URLByDeletingLastPathComponent] path];
27 | _localPath = [self gitLocalPath];
28 | }
29 | return self;
30 | }
31 |
32 | - (NSString *)filenameWithPathInCommit:(NSString *)commitHash {
33 | NSArray *args = @[@"show", @"--name-only", @"--pretty=format:", commitHash];
34 | NSString *files = [self outputGitWithArguments:args inPath:self.documentDirectoryPath];
35 |
36 | NSString *documentFilename = [self.documentURL lastPathComponent];
37 | NSString *filenameWithPathInCommit = nil;
38 | for (NSString *filenameWithPath in [files componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]])
39 | {
40 | if ([filenameWithPath hasSuffix:documentFilename])
41 | {
42 | filenameWithPathInCommit = [filenameWithPath stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
43 | break;
44 | }
45 | }
46 |
47 | if (!filenameWithPathInCommit)
48 | {
49 | LZLog(@"Unable to find file in commit.");
50 | return nil;
51 | }
52 |
53 | return filenameWithPathInCommit;
54 | }
55 |
56 | - (NSString *)latestCommitHash {
57 | NSString *documentFullPath = [self.documentURL path];
58 | // Get last commit hash
59 | NSArray *args = @[@"log", @"-n1", @"--no-decorate", documentFullPath];
60 | NSString *rawLastCommitHash = [self outputGitWithArguments:args inPath:self.documentDirectoryPath];
61 | LZLog(@"GIT log: %@", rawLastCommitHash);
62 | NSArray *commitHashInfo = [rawLastCommitHash componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
63 |
64 | if (commitHashInfo.count < 2)
65 | {
66 | // [RIGUtils showMessage:@"Unable to find lastest commit."];
67 | return nil;
68 | }
69 |
70 | NSString *commitHash = [commitHashInfo objectAtIndex:1];
71 | return commitHash;
72 | }
73 |
74 | - (NSString *)gitLocalPath {
75 | NSURL *activeDocumentURL = self.documentURL;
76 | NSString *activeDocumentDirectoryPath = [[activeDocumentURL URLByDeletingLastPathComponent] path];
77 | NSArray *args = @[@"rev-parse", @"--show-toplevel"];
78 | NSString *rootPath = [self outputGitWithArguments:args inPath:activeDocumentDirectoryPath];
79 | return [rootPath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
80 | }
81 |
82 | // Performs a git command with given args in the given directory
83 | - (NSString *)outputGitWithArguments:(NSArray *)args inPath:(NSString *)path
84 | {
85 | if (path.length == 0)
86 | {
87 | LZLog(@"Invalid path for git working directory.");
88 | return nil;
89 | }
90 |
91 | NSTask *task = [[NSTask alloc] init];
92 | task.launchPath = @"/usr/bin/xcrun";
93 | task.currentDirectoryPath = path;
94 | task.arguments = [@[@"git", @"--no-pager"] arrayByAddingObjectsFromArray:args];
95 | task.standardOutput = [NSPipe pipe];
96 | NSFileHandle *file = [task.standardOutput fileHandleForReading];
97 |
98 | [task launch];
99 |
100 | // For some reason [task waitUntilExit]; does not return sometimes. Therefore this rather hackish solution:
101 | int count = 0;
102 | while (task.isRunning && (count < 10))
103 | {
104 | [NSThread sleepForTimeInterval:0.1];
105 | count++;
106 | }
107 |
108 | NSString *output = [[NSString alloc] initWithData:[file readDataToEndOfFile] encoding:NSUTF8StringEncoding];
109 |
110 | return output;
111 | }
112 |
113 | - (NSString *)remotePathFromRemoteURL:(NSString *)remotePath {
114 | // Check for SSH protocol
115 | NSRange begin = [remotePath rangeOfString:@"git@"];
116 |
117 | if (begin.location == NSNotFound)
118 | {
119 | // SSH protocol not found, check for GIT protocol
120 | begin = [remotePath rangeOfString:@"git://"];
121 | }
122 | if (begin.location == NSNotFound)
123 | {
124 | // HTTPS protocol check
125 | begin = [remotePath rangeOfString:@"https://"];
126 | }
127 | if (begin.location == NSNotFound)
128 | {
129 | // HTTP protocol check
130 | begin = [remotePath rangeOfString:@"http://"];
131 | }
132 |
133 | NSRange end = [remotePath rangeOfString:@".git (fetch)"];
134 |
135 | if (end.location == NSNotFound)
136 | {
137 | // Alternate remote url end
138 | end = [remotePath rangeOfString:@" (fetch)"];
139 | }
140 |
141 | if ((begin.location != NSNotFound) &&
142 | (end.location != NSNotFound))
143 | {
144 | NSUInteger githubURLBegin = begin.location + begin.length;
145 | NSUInteger githubURLLength = end.location - githubURLBegin;
146 | return [[remotePath
147 | substringWithRange:NSMakeRange(githubURLBegin, githubURLLength)]
148 | stringByReplacingOccurrencesOfString:@":" withString:@"/"];
149 | } else {
150 | return nil;
151 | }
152 | }
153 |
154 | #pragma mark - Remote Repo
155 |
156 | - (NSString *)remoteRepoUrl {
157 | RIGSetting *setting = [RIGSetting settingForGitPath:self.localPath];
158 | NSString *defaultRepo = [setting defaultRepo];
159 | if (defaultRepo != nil) {
160 | return defaultRepo;
161 | } else {
162 | NSString *selectedRepo = [self getOrAskRemoteRepoUrl];
163 | [setting setDefaultRepo:selectedRepo];
164 | return selectedRepo;
165 | }
166 | }
167 |
168 | - (NSString *)getOrAskRemoteRepoUrl {
169 | NSString *rootPath = [self localPath];
170 | // Get GitHub username and repo name
171 | NSArray *args = @[@"remote", @"--verbose"];
172 | NSString *output = [self outputGitWithArguments:args inPath:rootPath];
173 | NSArray *remoteURLs = [output componentsSeparatedByString:@"\n"];
174 |
175 | NSMutableSet *remotePaths = [NSMutableSet set];
176 |
177 | for (NSString *remoteURL in remoteURLs)
178 | {
179 | NSString *remotePath = [self remotePathFromRemoteURL:remoteURL];
180 | if (remotePath) {
181 | [remotePaths addObject:remotePath];
182 | }
183 | }
184 |
185 | NSString *selectedRemotePath;
186 | if (remotePaths.count == 1) {
187 | selectedRemotePath = [remotePaths allObjects][0];
188 | } else if (remotePaths.count > 1) {
189 | NSArray *sortedRemotePaths = remotePaths.allObjects;
190 |
191 | // Ask the user what remote to use.
192 | // Attention: Due to NSRunAlert maximal three remotes are supported.
193 |
194 | NSAlert *alert = [[NSAlert alloc] init];
195 | alert.alertStyle = NSInformationalAlertStyle;
196 | alert.messageText = [NSString stringWithFormat:@"This repository has %ld remotes configured. Which one do you want to open?", remotePaths.count];
197 | [alert addButtonWithTitle:[sortedRemotePaths objectAtIndex:0]];
198 | [alert addButtonWithTitle:[sortedRemotePaths objectAtIndex:1]];
199 | [alert addButtonWithTitle:(sortedRemotePaths.count > 2 ? [sortedRemotePaths objectAtIndex:2] : nil)];
200 |
201 | NSModalResponse button = [alert runModal];
202 | if (button == NSAlertFirstButtonReturn) {
203 | selectedRemotePath = sortedRemotePaths[0];
204 | } else if (button == NSAlertSecondButtonReturn) {
205 | selectedRemotePath = sortedRemotePaths[1];
206 | } else if (button == NSAlertThirdButtonReturn) {
207 | selectedRemotePath = sortedRemotePaths[2];
208 | }
209 | }
210 |
211 | if (selectedRemotePath.length == 0)
212 | {
213 | LZLog(@"Unable to find github remote URL.");
214 | return nil;
215 | }
216 |
217 | NSString *fullUrl = [NSString stringWithFormat:@"https://%@", selectedRemotePath];
218 |
219 | return fullUrl;
220 | }
221 |
222 | - (BOOL)isValid {
223 | if (!self.localPath) {
224 | return NO;
225 | }
226 | return YES;
227 | }
228 |
229 | @end
230 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGPlugin.h:
--------------------------------------------------------------------------------
1 | //
2 | // GitHubKitPlugin.h
3 | // GitHubKitPlugin
4 | //
5 | // Created by lzw on 15/10/22.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RIGPlugin : NSObject
12 |
13 | + (instancetype)shared;
14 |
15 | @end
16 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGPlugin.m:
--------------------------------------------------------------------------------
1 | //
2 | // RIGPlugin.m
3 | //
4 | // Created by lzw on 15/10/22.
5 | // Copyright © 2015年 lzwjava. All rights reserved.
6 | //
7 |
8 | #import "RIGPlugin.h"
9 | #import "RIGSettingWindowController.h"
10 | #import "RIGConfig.h"
11 | #import "RIGUtils.h"
12 | #import "RIGGitRepo.h"
13 | #import "RIGSetting.h"
14 |
15 | id objc_getClass(const char* name);
16 |
17 | #define kRIGMenuToInsert @"Window"
18 |
19 | #define kPatternGitRemoteUrl @"{git_remote_url}"
20 |
21 | static Class DVTSourceTextViewClass;
22 | static Class IDEWorkspaceWindowControllerClass;
23 |
24 | @interface RIGPlugin()
25 |
26 | @property (nonatomic, strong) id ideWorkspaceWindow;
27 | @property (nonatomic, strong) id sourceTextView;
28 | @property (nonatomic, assign) NSUInteger selectionStartLineNumber;
29 | @property (nonatomic, assign) NSUInteger selectionEndLineNumber;
30 | @property (nonatomic, assign) BOOL useHTTPS;
31 |
32 | @property (nonatomic, strong) RIGSettingWindowController *setttingController;
33 |
34 | @property (nonatomic, strong) RIGGitRepo *gitRepo;
35 |
36 | @end
37 |
38 | @implementation RIGPlugin
39 |
40 | + (void)pluginDidLoad:(NSBundle *)plugin {
41 | DVTSourceTextViewClass = objc_getClass("DVTSourceTextView");
42 | IDEWorkspaceWindowControllerClass = objc_getClass("IDEWorkspaceWindowController");
43 | [self shared];
44 | }
45 |
46 | #pragma mark - init
47 |
48 | - (instancetype)init {
49 | if (self = [super init]) {
50 | [self addNotification];
51 | }
52 | return self;
53 | }
54 |
55 | + (instancetype)shared {
56 | static dispatch_once_t onceToken;
57 | static id instance = nil;
58 | dispatch_once(&onceToken, ^{
59 | instance = [[self alloc] init];
60 | });
61 | return instance;
62 | }
63 |
64 | - (void)dealloc {
65 | [[NSNotificationCenter defaultCenter] removeObserver:self];
66 | }
67 |
68 | #pragma mark - Menu init
69 |
70 | - (BOOL)menuExists {
71 | NSMenuItem *windowMenuItem = [[NSApp mainMenu] itemWithTitle:kRIGMenuToInsert];
72 | NSMenuItem *menu = [windowMenuItem.submenu itemWithTitle:@"Reveal In GitHub"];
73 | return menu != nil;
74 | }
75 |
76 | - (NSMenu *)githubMenu
77 | {
78 | NSMenuItem *windowMenuItem = [[NSApp mainMenu] itemWithTitle:kRIGMenuToInsert];
79 |
80 | [[windowMenuItem submenu] addItem:[NSMenuItem separatorItem]];
81 | NSMenuItem *githubMenuItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Reveal In GitHub" action:NULL keyEquivalent:@""];
82 | githubMenuItem.enabled = YES;
83 | githubMenuItem.submenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"Reveal In GitHub"];
84 | [windowMenuItem.submenu addItem:githubMenuItem];
85 |
86 | return githubMenuItem.submenu;
87 | }
88 |
89 | - (void)applicationDidFinishLaunching:(NSNotification *)noti {
90 | [self addMenu];
91 | }
92 |
93 | - (void)addMenu {
94 | if ([self menuExists]) {
95 | return;
96 | }
97 |
98 | NSMenu *githubMenu = [self githubMenu];
99 |
100 | NSMenuItem *settings = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"Settings" action:@selector(showSettingWindow:) keyEquivalent:@"S"];
101 | [settings setKeyEquivalentModifierMask:NSCommandKeyMask | NSControlKeyMask];
102 | settings.target = self;
103 | [githubMenu addItem:settings];
104 |
105 | NSArray *configs = [RIGSetting setting].configs;
106 | for (RIGConfig *config in configs) {
107 | NSString *keyEquivalent = config.lastKey;
108 | if (keyEquivalent == nil) {
109 | keyEquivalent = @"";
110 | }
111 | NSMenuItem *configItem = [[NSMenuItem alloc] initWithTitle:config.menuTitle action:@selector(customMenusClicked:) keyEquivalent:keyEquivalent];
112 | if (keyEquivalent.length > 0) {
113 | [configItem setKeyEquivalentModifierMask:NSCommandKeyMask | NSControlKeyMask];
114 | }
115 | configItem.target = self;
116 | [githubMenu addItem:configItem];
117 | }
118 | }
119 |
120 | #pragma mark - Notification and Selectors
121 |
122 | - (void)addNotification {
123 | NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
124 | [nc addObserver:self selector:@selector(applicationDidFinishLaunching:) name:NSApplicationDidFinishLaunchingNotification object:nil];
125 |
126 | [nc addObserver:self selector:@selector(sourceTextViewSelectionDidChange:) name:NSTextViewDidChangeSelectionNotification object:nil];
127 |
128 | [nc addObserver:self selector:@selector(fetchActiveIDEWorkspaceWindow:) name:NSWindowDidUpdateNotification object:nil];
129 | }
130 |
131 | - (void)sourceTextViewSelectionDidChange:(NSNotification *)notification {
132 | id view = [notification object];
133 | if ([view isMemberOfClass:DVTSourceTextViewClass])
134 | {
135 | self.sourceTextView = view;
136 | }
137 | }
138 |
139 | - (void)fetchActiveIDEWorkspaceWindow:(NSNotification *)notification {
140 | id window = [notification object];
141 | if ([window isKindOfClass:[NSWindow class]] && [window isMainWindow])
142 | {
143 | self.ideWorkspaceWindow = window;
144 | }
145 | }
146 |
147 | #pragma mark - Xcode Part
148 |
149 | - (NSURL *)activeDocument
150 | {
151 | NSArray *windows = [IDEWorkspaceWindowControllerClass valueForKey:@"workspaceWindowControllers"];
152 | for (id workspaceWindowController in windows)
153 | {
154 | if ([workspaceWindowController valueForKey:@"workspaceWindow"] == self.ideWorkspaceWindow || windows.count == 1)
155 | {
156 | id document = [[workspaceWindowController valueForKey:@"editorArea"] valueForKey:@"primaryEditorDocument"];
157 | return [document fileURL];
158 | }
159 | }
160 |
161 | return nil;
162 | }
163 |
164 | - (void)findSelection {
165 | if (self.sourceTextView == nil) {
166 | return;
167 | }
168 | NSRange selectedRange = [self.sourceTextView selectedRange];
169 | NSString *sourceTextUntilSelection = [[self.sourceTextView string] substringWithRange:NSMakeRange(0, selectedRange.location)];
170 |
171 | self.selectionStartLineNumber = [[sourceTextUntilSelection componentsSeparatedByCharactersInSet:
172 | [NSCharacterSet newlineCharacterSet]] count];
173 |
174 | NSString *sourceTextSelection = [[self.sourceTextView string] substringWithRange:selectedRange];
175 | NSUInteger selectedLines = [[sourceTextSelection componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] count];
176 | self.selectionEndLineNumber = self.selectionStartLineNumber + (selectedLines > 1 ? selectedLines - 1 : 0);
177 | }
178 |
179 | - (NSString *)selectedLineString {
180 | NSRange selectedRange = [self.sourceTextView selectedRange];
181 | NSString *sourceTextUntilSelection = [[self.sourceTextView string] substringWithRange:NSMakeRange(0, selectedRange.location)];
182 |
183 | NSArray *components = [sourceTextUntilSelection componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
184 | NSInteger selectedLineNumber = components.count;
185 |
186 | NSString *lineString = [[[self.sourceTextView string] componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] objectAtIndex:selectedLineNumber - 1];
187 | return lineString;
188 | }
189 |
190 | - (void)openUrl:(NSString *)url {
191 | NSTask *task = [NSTask launchedTaskWithLaunchPath:@"/usr/bin/open" arguments:@[url]];
192 | [task waitUntilExit];
193 | if (![task isRunning]) {
194 | int status = [task terminationStatus];
195 | if (status != 0 ) {
196 | NSModalResponse res = [RIGUtils showMessage:[NSString stringWithFormat:@"Cloud not open %@. It might be a bug. Please report it. Thanks.", url]];
197 | if (res == NSAlertFirstButtonReturn) {
198 | [NSTask launchedTaskWithLaunchPath:@"/usr/bin/open" arguments:@[@"https://github.com/lzwjava/Reveal-In-GitHub/issues/new"]];
199 | }
200 | }
201 | }
202 | }
203 |
204 | #pragma mark - Show Settings
205 |
206 | - (void)showSettingWindow:(id)sender {
207 | if (![self trySetGitRepo]) {
208 | return;
209 | }
210 | self.setttingController = [[RIGSettingWindowController alloc] initWithWindowNibName:@"RIGSettingWindowController"];
211 | self.setttingController.gitRepo = self.gitRepo;
212 | [self.setttingController showWindow:self.setttingController];
213 | }
214 |
215 | #pragma mark - Git Menu Actions
216 |
217 | - (NSDictionary *)currentRepoInfos {
218 | NSMutableDictionary *dict= [[NSMutableDictionary alloc] init];
219 | NSString *gitRemoteUrl = [self.gitRepo remoteRepoUrl];
220 | if (gitRemoteUrl) {
221 | [dict setObject:gitRemoteUrl forKey:kPatternGitRemoteUrl];
222 | }
223 | NSString *commit = [self.gitRepo latestCommitHash];
224 | if (commit) {
225 | [dict setObject:commit forKey:@"{commit}"];
226 | NSString *filePath = [self.gitRepo filenameWithPathInCommit:commit];
227 | if (filePath) {
228 | [dict setObject:filePath forKey:@"{file_path}"];
229 | }
230 | }
231 | NSString *selection = [self selectionString];
232 | if (selection) {
233 | [dict setObject:selection forKey:@"{selection}"];
234 | }
235 | return dict;
236 | }
237 |
238 | - (NSString *)selectionString {
239 | [self findSelection];
240 |
241 | NSUInteger start = self.selectionStartLineNumber;
242 | NSUInteger end = self.selectionEndLineNumber;
243 |
244 | if (start == end) {
245 | return [NSString stringWithFormat:@"L%ld", start];
246 | } else {
247 | return [NSString stringWithFormat:@"L%ld-L%ld", start, end];
248 | }
249 | }
250 |
251 | - (BOOL)trySetGitRepo {
252 | NSURL *activeDocumentURL = [self activeDocument];
253 | if (activeDocumentURL == nil) {
254 | [RIGUtils showMessage:@"No file is opening now."];
255 | return NO;
256 | }
257 | self.gitRepo = [[RIGGitRepo alloc] initWithDocumentURL:[self activeDocument]];
258 | if (![self.gitRepo isValid]) {
259 | [RIGUtils showMessage:@"Could not get git repo from current file."];
260 | return NO;
261 | }
262 | return YES;
263 | }
264 |
265 | - (void)customMenusClicked:(NSMenuItem *)menuItem {
266 | if (![self trySetGitRepo]) {
267 | return;
268 | }
269 |
270 | RIGConfig *currentConfig = nil;
271 | for (RIGConfig *confing in [RIGSetting setting].configs) {
272 | if ([confing.menuTitle isEqualToString:menuItem.title]) {
273 | currentConfig = confing;
274 | break;
275 | }
276 | }
277 |
278 | NSDictionary *infos = [self currentRepoInfos];
279 | if (infos[kPatternGitRemoteUrl] == nil) {
280 | [RIGUtils showMessage:@"Cannot find a git remote."];
281 | return;
282 | }
283 | NSMutableString *url = [[NSMutableString alloc] initWithString:currentConfig.pattern];
284 | for (NSString *key in [infos allKeys]) {
285 | NSString *value = [infos objectForKey:key];
286 | [url replaceOccurrencesOfString:key withString:value options:NSLiteralSearch range:NSMakeRange(0, url.length)];
287 | }
288 | [self openUrl:url];
289 | }
290 |
291 | @end
292 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGSetting.h:
--------------------------------------------------------------------------------
1 | //
2 | // RIGSettingsManager.h
3 | // Reveal-In-GitHub
4 | //
5 | // Created by lzw on 15/10/31.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface RIGSetting : NSObject
12 |
13 | + (RIGSetting *)setting;
14 | + (RIGSetting *)settingForGitPath:(NSString *)gitPath;
15 |
16 | @property (nonatomic, strong) NSArray *configs;
17 | @property (nonatomic, strong) NSString *defaultRepo;;
18 |
19 | + (NSArray *)defaultConfigs;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGSetting.m:
--------------------------------------------------------------------------------
1 | //
2 | // RIGSettingsManager.m
3 | // Reveal-In-GitHub
4 | //
5 | // Created by lzw on 15/10/31.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import "RIGSetting.h"
10 | #import "RIGConfig.h"
11 |
12 | #define kRIGDefaultRepo @"com.lzwjava.reveal-in-github.defaultRepo"
13 | #define kRIGConfigs @"com.lzwjava.reveal-in-github.configs"
14 |
15 | @interface RIGSetting()
16 |
17 | @property (nonatomic, strong) NSString *gitPath;
18 |
19 | @end
20 |
21 | @implementation RIGSetting
22 |
23 | + (RIGSetting *)setting {
24 | static dispatch_once_t once;
25 | static RIGSetting *defaultSetting;
26 | dispatch_once(&once, ^ {
27 | defaultSetting = [[RIGSetting alloc] init];
28 |
29 | NSDictionary *defaults = @{kRIGConfigs: [[self class] dictsForConfigs:[[self class] defaultConfigs]]};
30 | [[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
31 | });
32 | return defaultSetting;
33 | }
34 |
35 | + (RIGSetting *)settingForGitPath:(NSString *)gitPath {
36 | RIGSetting *setting = [[RIGSetting alloc] init];
37 | setting.gitPath = gitPath;
38 | return setting;
39 | }
40 |
41 | - (NSArray *)configs {
42 | NSArray *configDicts = [[NSUserDefaults standardUserDefaults] objectForKey:kRIGConfigs];
43 | NSMutableArray *configs = [NSMutableArray array];
44 | for (NSDictionary *configDict in configDicts) {
45 | RIGConfig *config = [[RIGConfig alloc] initWithDictionary:configDict];
46 | [configs addObject:config];
47 | }
48 | return configs;
49 | }
50 |
51 | - (void)setConfigs:(NSArray *)configs {
52 | NSArray *dicts = [[self class] dictsForConfigs:configs];
53 | [[NSUserDefaults standardUserDefaults] setObject:dicts forKey:kRIGConfigs];
54 | [[NSUserDefaults standardUserDefaults] synchronize];
55 | }
56 |
57 | + (NSArray *)dictsForConfigs:(NSArray *)configs {
58 | NSMutableArray *dicts = [NSMutableArray array];
59 | for (RIGConfig *config in configs) {
60 | NSDictionary *dict = [config dictionary];
61 | if (dict.count > 0) {
62 | [dicts addObject:dict];
63 | }
64 | }
65 | return dicts;
66 | }
67 |
68 | + (NSArray *)defaultConfigs {
69 | RIGConfig *config1 = [RIGConfig configWithMenuTitle:@"Repo" lastKey:@"R" pattern:@"{git_remote_url}"];
70 |
71 | RIGConfig *config2 = [RIGConfig configWithMenuTitle:@"Issues" lastKey:@"I" pattern:@"{git_remote_url}/issues"];
72 |
73 | RIGConfig *config3 = [RIGConfig configWithMenuTitle:@"PRs" lastKey:@"P" pattern:@"{git_remote_url}/pulls"];
74 |
75 | RIGConfig *config4 = [RIGConfig configWithMenuTitle:@"Quick File" lastKey:@"Q" pattern:@"{git_remote_url}/blob/{commit}/{file_path}#{selection}"];
76 |
77 | RIGConfig *config5 = [RIGConfig configWithMenuTitle:@"List History" lastKey:@"L" pattern:@"{git_remote_url}/commits/{commit}/{file_path}"];
78 |
79 | RIGConfig *config6 = [RIGConfig configWithMenuTitle:@"Blame" lastKey:@"B" pattern:@"{git_remote_url}/blame/{commit}/{file_path}#{selection}"];
80 |
81 | RIGConfig *config7 = [RIGConfig configWithMenuTitle:@"Notifications" lastKey:@"N" pattern:@"{git_remote_url}/notifications?all=1"];
82 |
83 | return @[config1, config2, config3, config4, config5, config6, config7];
84 | }
85 |
86 | #pragma mark -
87 |
88 | - (NSString *)defaultRepoKey {
89 | return [NSString stringWithFormat:@"%@:%@", kRIGDefaultRepo, self.gitPath];
90 | }
91 |
92 | - (void)setDefaultRepo:(NSString *)defaultRepo {
93 | NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
94 | if (defaultRepo == nil) {
95 | [ud removeObjectForKey:[self defaultRepoKey]];
96 | } else {
97 | [ud setObject:defaultRepo forKey:[self defaultRepoKey]];
98 | }
99 | [ud synchronize];
100 | }
101 |
102 | - (NSString *)defaultRepo {
103 | return [[NSUserDefaults standardUserDefaults] stringForKey:[self defaultRepoKey]];
104 | }
105 |
106 | @end
107 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGSettingWindowController.h:
--------------------------------------------------------------------------------
1 | //
2 | // RIGSettingWindowController.h
3 | // Reveal-In-GitHub
4 | //
5 | // Created by lzw on 15/10/28.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "RIGGitRepo.h"
11 |
12 | @interface RIGSettingWindowController : NSWindowController
13 |
14 | @property (nonatomic, strong) RIGGitRepo *gitRepo;
15 |
16 | @end
17 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGSettingWindowController.m:
--------------------------------------------------------------------------------
1 | //
2 | // RIGSettingWindowController.m
3 | // Reveal-In-GitHub
4 | //
5 | // Created by lzw on 15/10/28.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import "RIGSettingWindowController.h"
10 | #import "RIGConfigCellsView.h"
11 | #import "RIGConfig.h"
12 | #import "RIGPlugin.h"
13 | #import "RIGUtils.h"
14 | #import "RIGSetting.h"
15 |
16 | #define kOutterXMargin 0
17 | #define kOutterYMargin 0
18 |
19 | @interface RIGSettingWindowController ()
20 |
21 | @property (nonatomic, strong) NSArray *configs;
22 | @property (nonatomic, strong) RIGConfigCellsView *configCellsView;
23 | @property (weak) IBOutlet NSView *mainView;
24 | @property (weak) IBOutlet NSView *configsView;
25 |
26 | @end
27 |
28 | @implementation RIGSettingWindowController
29 |
30 | - (void)awakeFromNib {
31 | [super awakeFromNib];
32 | }
33 |
34 | - (void)windowDidLoad {
35 | [super windowDidLoad];
36 |
37 | // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
38 |
39 | self.configs = [self displayConfigs];
40 |
41 | self.configCellsView = [[RIGConfigCellsView alloc] initWithFrame:CGRectMake(kOutterXMargin, kOutterYMargin, CGRectGetWidth(self.configsView.frame) - 2 * kOutterXMargin, [RIGConfigCellsView heightForConfigs:self.configs])];
42 | self.configCellsView.configs = self.configs;
43 | [self.configsView addSubview:self.configCellsView];
44 | // [self updateConfigsViewHeight];
45 | [self.configCellsView reloadData];
46 | }
47 |
48 | - (void)updateConfigsViewHeight {
49 | CGRect frame = self.configsView.frame;
50 | frame.size.height = CGRectGetHeight(self.configCellsView.frame);
51 | self.configsView.frame = frame;
52 | }
53 |
54 | - (NSMutableArray *)displayConfigs {
55 | NSMutableArray *configs = [NSMutableArray arrayWithArray:[RIGSetting setting].configs];
56 | while (configs.count < 10) {
57 | RIGConfig *config = [[RIGConfig alloc] init];
58 | config.menuTitle = @"";
59 | config.lastKey = @"";
60 | config.pattern = @"";
61 | [configs addObject:config];
62 | }
63 | return configs;
64 | }
65 |
66 | - (void)reloadConfigs {
67 | self.configs = [self displayConfigs];
68 | self.configCellsView.configs = self.configs;
69 | [self.configCellsView reloadData];
70 | }
71 |
72 | - (BOOL)isValidConfigs:(NSArray *)configs {
73 | for (RIGConfig *config in configs) {
74 | if (![config isValid]) {
75 | return NO;
76 | }
77 | }
78 | return YES;
79 | }
80 |
81 | - (NSArray *)filteredConfigs {
82 | NSMutableArray *filtered = [NSMutableArray array];
83 | NSArray *configs = self.configCellsView.configs;
84 | for (RIGConfig *config in configs) {
85 | if (config.menuTitle.length > 0 || config.lastKey.length > 0 || config.pattern.length > 0) {
86 | [filtered addObject:config];
87 | }
88 | }
89 | return filtered;
90 | }
91 |
92 | - (IBAction)saveButtonClcked:(id)sender {
93 | NSArray *configs = [self filteredConfigs];
94 | if (![self isValidConfigs:configs]) {
95 | [RIGUtils showMessage:@"Please complete the config, should at least have menuTitle and pattern."];
96 | return;
97 | }
98 | [RIGSetting setting].configs = self.configCellsView.configs;
99 | [RIGUtils showMessage:@"Save succeed. Will Take effect when reopen Xcode."];
100 | }
101 |
102 | - (IBAction)clearButtonClicked:(id)sender {
103 | RIGSetting *setting = [RIGSetting settingForGitPath:self.gitRepo.localPath];
104 | NSString *defaultRepo = setting.defaultRepo;
105 | if (defaultRepo == nil) {
106 | [RIGUtils showMessage:@"There's no default repo setting."];
107 | } else {
108 | setting.defaultRepo = nil;
109 | [RIGUtils showMessage:[NSString stringWithFormat:@"Succeed to clear current default repo(%@) setting. In the next time to open github, will ask you to select new default repo.", defaultRepo]];
110 | }
111 | }
112 |
113 | - (IBAction)resetMenusButtonClicked:(id)sender {
114 | [[RIGSetting setting] setConfigs:[RIGSetting defaultConfigs]];
115 | [self reloadConfigs];
116 | }
117 |
118 | @end
119 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGSettingWindowController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
45 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGUtils.h:
--------------------------------------------------------------------------------
1 | //
2 | // RIGUtils.h
3 | // Reveal-In-GitHub
4 | //
5 | // Created by lzw on 15/10/31.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | #ifdef DEBUG
13 | # define LZLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
14 | #else
15 | # define LZLog(fmt, ...)
16 | #endif
17 |
18 | @interface RIGUtils : NSObject
19 |
20 | + (NSModalResponse)showMessage:(NSString *)message;
21 |
22 | @end
23 |
--------------------------------------------------------------------------------
/Reveal-In-GitHub/RIGUtils.m:
--------------------------------------------------------------------------------
1 | //
2 | // RIGUtils.m
3 | // Reveal-In-GitHub
4 | //
5 | // Created by lzw on 15/10/31.
6 | // Copyright © 2015年 lzwjava. All rights reserved.
7 | //
8 |
9 | #import "RIGUtils.h"
10 | #import
11 |
12 | @implementation RIGUtils
13 |
14 | + (NSModalResponse)showMessage:(NSString *)message {
15 | NSAlert *alert = [[NSAlert alloc] init];
16 | [alert addButtonWithTitle:@"OK"];
17 | [alert setMessageText: message];
18 | [alert setAlertStyle:NSWarningAlertStyle];
19 | return [alert runModal];
20 | }
21 |
22 | @end
23 |
--------------------------------------------------------------------------------