Skip to content

Commit 6f8f30b

Browse files
committed
Add support for Obsidian Advanced URI (#4)
1 parent 1d3dea8 commit 6f8f30b

12 files changed

+509
-271
lines changed

ObsidianShell.CLI/ObsidianShell.CLI.csproj

-8
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,6 @@
7676
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
7777
</PropertyGroup>
7878
<ItemGroup>
79-
<Reference Include="Fastenshtein, Version=1.0.0.8, Culture=neutral, processorArchitecture=MSIL">
80-
<HintPath>..\packages\Fastenshtein.1.0.0.8\lib\net452\Fastenshtein.dll</HintPath>
81-
</Reference>
82-
<Reference Include="NCode.ReparsePoints, Version=1.0.2.0, Culture=neutral, processorArchitecture=MSIL">
83-
<HintPath>..\packages\NCode.ReparsePoints.1.0.2\lib\net451\NCode.ReparsePoints.dll</HintPath>
84-
</Reference>
8579
<Reference Include="System" />
8680
<Reference Include="System.Configuration" />
8781
<Reference Include="System.Core" />
@@ -93,13 +87,11 @@
9387
<Reference Include="System.Xml" />
9488
</ItemGroup>
9589
<ItemGroup>
96-
<Compile Include="PathPrefix.cs" />
9790
<Compile Include="Program.cs" />
9891
<Compile Include="Properties\AssemblyInfo.cs" />
9992
</ItemGroup>
10093
<ItemGroup>
10194
<None Include="App.config" />
102-
<None Include="packages.config" />
10395
<None Include="Properties\app.manifest" />
10496
</ItemGroup>
10597
<ItemGroup>

ObsidianShell.CLI/Program.cs

+6-244
Original file line numberDiff line numberDiff line change
@@ -7,268 +7,30 @@
77
using System.Text;
88
using System.Threading.Tasks;
99
using System.Windows.Forms;
10-
using NCode.ReparsePoints;
1110

1211
namespace ObsidianShell.CLI
1312
{
1413
internal class Program
1514
{
1615
static Settings _settings;
17-
16+
1817
static void Main(string[] args)
1918
{
20-
AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) => {
19+
AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) =>
20+
{
2121
MessageBox.Show(eventArgs.ExceptionObject.ToString(), "ObsidianShell.CLI", MessageBoxButtons.OK, MessageBoxIcon.Error);
2222
};
2323

24-
_settings = Settings.Load();
25-
2624
if (args.Length == 0)
2725
{
2826
Process.Start("obsidian://open");
2927
return;
3028
}
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-
}
9129

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();
26831

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]);
27234
}
27335
}
27436
}

ObsidianShell.CLI/packages.config

-5
This file was deleted.

ObsidianShell.GUI/MainViewModel.cs

+13-1
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@ internal class MainViewModel : INotifyPropertyChanged
2525

2626
public ObservableCollection<OpenModeView> OpenModeViews { get; }
2727

28-
public OpenModeView CurrentOpenModeView {
28+
public OpenModeView CurrentOpenModeView
29+
{
2930
get => OpenModeViews[(int)Settings.OpenMode];
3031
set => Settings.OpenMode = value.OpenMode;
3132
}
3233

34+
public ObsidianOpenMode[] ObsidianOpenModes { get; }
35+
3336
public MainViewModel()
3437
{
3538
Settings = Settings.Load();
@@ -57,6 +60,15 @@ public MainViewModel()
5760
EnableRecentVault = true
5861
}
5962
};
63+
64+
ObsidianOpenModes = new[]
65+
{
66+
ObsidianOpenMode.CurrentTab,
67+
ObsidianOpenMode.NewTab,
68+
ObsidianOpenMode.NewWindow,
69+
ObsidianOpenMode.NewPane,
70+
ObsidianOpenMode.HoverPopover
71+
};
6072
}
6173

6274
public void Apply()

0 commit comments

Comments
 (0)