|
7 | 7 | using System.Text;
|
8 | 8 | using System.Threading.Tasks;
|
9 | 9 | using System.Windows.Forms;
|
10 |
| -using NCode.ReparsePoints; |
11 | 10 |
|
12 | 11 | namespace ObsidianShell.CLI
|
13 | 12 | {
|
14 | 13 | internal class Program
|
15 | 14 | {
|
16 | 15 | static Settings _settings;
|
17 |
| - |
| 16 | + |
18 | 17 | static void Main(string[] args)
|
19 | 18 | {
|
20 |
| - AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) => { |
| 19 | + AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) => |
| 20 | + { |
21 | 21 | MessageBox.Show(eventArgs.ExceptionObject.ToString(), "ObsidianShell.CLI", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
22 | 22 | };
|
23 | 23 |
|
24 |
| - _settings = Settings.Load(); |
25 |
| - |
26 | 24 | if (args.Length == 0)
|
27 | 25 | {
|
28 | 26 | Process.Start("obsidian://open");
|
29 | 27 | return;
|
30 | 28 | }
|
31 |
| - |
32 |
| - string path = args[0]; |
33 |
| - |
34 |
| - switch (_settings.OpenMode) |
35 |
| - { |
36 |
| - case OpenMode.VaultFallback: |
37 |
| - { |
38 |
| - if (IsFileInVault(path)) |
39 |
| - { |
40 |
| - OpenFileInVault(path); |
41 |
| - } |
42 |
| - else |
43 |
| - { |
44 |
| - OpenFileByFallback(path); |
45 |
| - } |
46 |
| - break; |
47 |
| - } |
48 |
| - case OpenMode.VaultRecent: |
49 |
| - { |
50 |
| - if (IsFileInVault(path)) |
51 |
| - { |
52 |
| - OpenFileInVault(path); |
53 |
| - } |
54 |
| - else |
55 |
| - { |
56 |
| - OpenFileInRecent(path); |
57 |
| - } |
58 |
| - break; |
59 |
| - } |
60 |
| - case OpenMode.Recent: |
61 |
| - { |
62 |
| - OpenFileInRecent(path); |
63 |
| - break; |
64 |
| - } |
65 |
| - } |
66 |
| - } |
67 |
| - |
68 |
| - static bool IsFileInVault(string path) |
69 |
| - { |
70 |
| - // "This constructor does not check if a directory exists." |
71 |
| - var directory = new DirectoryInfo(path); |
72 |
| - if (directory.Exists) |
73 |
| - { |
74 |
| - if (Directory.Exists(directory.FullName + @"\.obsidian")) |
75 |
| - { |
76 |
| - return true; |
77 |
| - } |
78 |
| - } |
79 |
| - |
80 |
| - directory = directory.Parent; |
81 |
| - while (directory is not null) |
82 |
| - { |
83 |
| - if (Directory.Exists(directory.FullName + @"\.obsidian")) |
84 |
| - { |
85 |
| - return true; |
86 |
| - } |
87 |
| - directory = directory.Parent; |
88 |
| - } |
89 |
| - return false; |
90 |
| - } |
91 | 29 |
|
92 |
| - static void OpenFileInVault(string path) |
93 |
| - { |
94 |
| - // WebUtility.UrlEncode() replaces space with "+" instead of "%20" |
95 |
| - // Uri.EscapeUriString() replaces space with "%20" but does not replace most reserved characters (!#$&'()+,;=@[]) |
96 |
| - Process.Start($"obsidian://open?path={Uri.EscapeDataString(path)}"); |
97 |
| - } |
98 |
| - |
99 |
| - static void OpenFileByFallback(string path) |
100 |
| - { |
101 |
| - // Process.Start() will not expand environment variables |
102 |
| - string editor = Environment.ExpandEnvironmentVariables(_settings.FallbackMarkdownEditor); |
103 |
| - |
104 |
| - // the filename and arguments must be provided seperately when calling Process.Start() |
105 |
| - Process.Start(editor, String.Format(_settings.FallbackMarkdownEditorArguments, $@"""{path}""")); |
106 |
| - } |
107 |
| - |
108 |
| - static void OpenFileInRecent(string path) |
109 |
| - { |
110 |
| - // hard link stays valid when the source file is deleted; |
111 |
| - // symbolic link requires SeCreateSymbolicLinkPrivilege; |
112 |
| - // so we use junction for simplicity |
113 |
| - |
114 |
| - string path_in_recent; |
115 |
| - |
116 |
| - var directory = new DirectoryInfo(path); |
117 |
| - if (directory.Exists) |
118 |
| - { |
119 |
| - // find the Markdown file with the minimum edit distance from the directory name |
120 |
| - Fastenshtein.Levenshtein edit_distance = new Fastenshtein.Levenshtein(directory.Name); |
121 |
| - int min_distance = int.MaxValue; |
122 |
| - FileInfo most_similar_file = null; |
123 |
| - foreach (FileInfo file in directory.EnumerateFiles("*.md")) |
124 |
| - { |
125 |
| - int distance = edit_distance.DistanceFrom(file.Name); |
126 |
| - if (distance < min_distance) |
127 |
| - { |
128 |
| - most_similar_file = file; |
129 |
| - min_distance = distance; |
130 |
| - } |
131 |
| - } |
132 |
| - |
133 |
| - path_in_recent = CreateLinkInRecent(directory, true); |
134 |
| - |
135 |
| - if (most_similar_file is null is false) |
136 |
| - path_in_recent += '\\' + most_similar_file.Name; |
137 |
| - } |
138 |
| - else |
139 |
| - { |
140 |
| - path_in_recent = CreateLinkInRecent(directory.Parent, false) + '\\' + directory.Name; |
141 |
| - } |
142 |
| - |
143 |
| - OpenFileInVault(path_in_recent); |
144 |
| - } |
145 |
| - |
146 |
| - static string FormatLinkName(string prefixed_name, bool explicitDirectory) |
147 |
| - { |
148 |
| - string s = ""; |
149 |
| - |
150 |
| - if (prefixed_name[0] == '.') |
151 |
| - s += ' '; |
152 |
| - |
153 |
| - s += prefixed_name.Replace('\\', '\'); |
154 |
| - |
155 |
| - if (explicitDirectory) |
156 |
| - s += "\"; |
157 |
| - |
158 |
| - return s; |
159 |
| - } |
160 |
| - |
161 |
| - static string CreateLinkInRecent(DirectoryInfo directory, bool explicitDirectory) |
162 |
| - { |
163 |
| - var provider = ReparsePointFactory.Provider; |
164 |
| - DirectoryInfo recent = new DirectoryInfo(_settings.RecentVault); |
165 |
| - |
166 |
| - string dir_target_same = null; |
167 |
| - List<string> conflict_dirs = new List<string>(); |
168 |
| - List<string> conflict_targets = new List<string>(); |
169 |
| - bool TestTarget(string path) |
170 |
| - { |
171 |
| - ReparseLink link = provider.GetLink(path); |
172 |
| - if (link.Target is null) |
173 |
| - return false; |
174 |
| - /* |
175 |
| - if (!Directory.Exists(link.Target)) |
176 |
| - { |
177 |
| - Directory.Delete(path); |
178 |
| - return false; |
179 |
| - } |
180 |
| - */ |
181 |
| - |
182 |
| - if (path.EndsWith($"\\{directory.Name}") || path.EndsWith($"\{directory.Name}")) |
183 |
| - { |
184 |
| - if (link.Target == directory.FullName) |
185 |
| - { |
186 |
| - dir_target_same = path; |
187 |
| - return true; |
188 |
| - } |
189 |
| - |
190 |
| - if (!Directory.Exists(link.Target)) |
191 |
| - { |
192 |
| - Directory.Delete(path); |
193 |
| - return false; |
194 |
| - } |
195 |
| - |
196 |
| - conflict_dirs.Add(path); |
197 |
| - conflict_targets.Add(link.Target); |
198 |
| - } |
199 |
| - else |
200 |
| - { |
201 |
| - // If the parent directory of a directory is already indexed by Obsidian, the directory won't be indexed before restarting Obsidian. |
202 |
| - // The same is true even for a directory whose subdirectories are already indexed. |
203 |
| - |
204 |
| - if (directory.FullName.StartsWith(link.Target)) { |
205 |
| - if (path.EndsWith("\")) |
206 |
| - { |
207 |
| - dir_target_same = path + directory.FullName.Substring(link.Target.Length); |
208 |
| - return true; |
209 |
| - } |
210 |
| - else |
211 |
| - { |
212 |
| - Directory.Delete(path); |
213 |
| - return false; |
214 |
| - } |
215 |
| - } |
216 |
| - |
217 |
| - if (link.Target.StartsWith(directory.FullName)) |
218 |
| - { |
219 |
| - Directory.Delete(path); |
220 |
| - return false; |
221 |
| - } |
222 |
| - } |
223 |
| - return false; |
224 |
| - } |
225 |
| - string formatted_name = FormatLinkName(directory.Name, false); |
226 |
| - if (Directory.Exists(recent.FullName + $@"\{formatted_name}") && TestTarget(recent.FullName + $@"\{formatted_name}")) |
227 |
| - return dir_target_same; |
228 |
| - string formatted_name_explicit = FormatLinkName(directory.Name, true); |
229 |
| - if (Directory.Exists(recent.FullName + $@"\{formatted_name_explicit}") && TestTarget(recent.FullName + $@"\{formatted_name_explicit}")) |
230 |
| - return dir_target_same; |
231 |
| - foreach (DirectoryInfo dir in recent.EnumerateDirectories()) // $"*\{directory.Name}" |
232 |
| - { |
233 |
| - // reduce unnecessary IO operations |
234 |
| - if (dir.Name == ".obsidian" || dir.Name == formatted_name || dir.Name == formatted_name_explicit) |
235 |
| - continue; |
236 |
| - |
237 |
| - if (TestTarget(dir.FullName)) |
238 |
| - return dir_target_same; |
239 |
| - } |
240 |
| - |
241 |
| - conflict_targets.Add(directory.FullName); |
242 |
| - List<string> prefixed_dirs = PathPrefix.PathPrefixed(conflict_targets); |
243 |
| - string path_in_recent = recent.FullName + '\\'; |
244 |
| - void Move(string source, string dest) |
245 |
| - { |
246 |
| - if (source == dest) |
247 |
| - return; |
248 |
| - Directory.Move(source, dest); |
249 |
| - } |
250 |
| - for (int i = 0; i < conflict_dirs.Count; i++) |
251 |
| - { |
252 |
| - // may conflict? |
253 |
| - Move(conflict_dirs[i], path_in_recent + FormatLinkName(prefixed_dirs[i], conflict_dirs[i].EndsWith("\"))); |
254 |
| - /* |
255 |
| - Directory.Delete(conflict_dirs[i]); |
256 |
| - provider.CreateLink(path_in_recent + prefixed_dirs[i].Replace('\\', '\'), conflict_targets[i], LinkType.Junction); |
257 |
| - */ |
258 |
| - } |
259 |
| - |
260 |
| - // Recent = dirs + .obsidian |
261 |
| - if (recent.GetDirectories().Length > _settings.RecentVaultSubdirectoriesLimit) |
262 |
| - { |
263 |
| - (from f in recent.GetDirectories() |
264 |
| - orderby f.LastWriteTime ascending |
265 |
| - where f.Name != ".obsidian" |
266 |
| - select f).First().Delete(); |
267 |
| - } |
| 30 | + _settings = Settings.Load(); |
268 | 31 |
|
269 |
| - path_in_recent += FormatLinkName(prefixed_dirs.Last(), explicitDirectory); |
270 |
| - provider.CreateLink(path_in_recent, directory.FullName, LinkType.Junction); |
271 |
| - return path_in_recent; |
| 32 | + Obsidian obsidian = new Obsidian(_settings); |
| 33 | + obsidian.OpenFile(args[0]); |
272 | 34 | }
|
273 | 35 | }
|
274 | 36 | }
|
0 commit comments