Skip to content

Commit

Permalink
Add support for Obsidian Advanced URI (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
Chaoses-Ib committed Mar 12, 2023
1 parent 1d3dea8 commit 6f8f30b
Show file tree
Hide file tree
Showing 12 changed files with 509 additions and 271 deletions.
8 changes: 0 additions & 8 deletions ObsidianShell.CLI/ObsidianShell.CLI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,6 @@
<ApplicationManifest>Properties\app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<Reference Include="Fastenshtein, Version=1.0.0.8, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Fastenshtein.1.0.0.8\lib\net452\Fastenshtein.dll</HintPath>
</Reference>
<Reference Include="NCode.ReparsePoints, Version=1.0.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\NCode.ReparsePoints.1.0.2\lib\net451\NCode.ReparsePoints.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
Expand All @@ -93,13 +87,11 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="PathPrefix.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
<None Include="Properties\app.manifest" />
</ItemGroup>
<ItemGroup>
Expand Down
250 changes: 6 additions & 244 deletions ObsidianShell.CLI/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,268 +7,30 @@
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using NCode.ReparsePoints;

namespace ObsidianShell.CLI
{
internal class Program
{
static Settings _settings;

static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) => {
AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) =>
{
MessageBox.Show(eventArgs.ExceptionObject.ToString(), "ObsidianShell.CLI", MessageBoxButtons.OK, MessageBoxIcon.Error);
};

_settings = Settings.Load();

if (args.Length == 0)
{
Process.Start("obsidian://open");
return;
}

string path = args[0];

switch (_settings.OpenMode)
{
case OpenMode.VaultFallback:
{
if (IsFileInVault(path))
{
OpenFileInVault(path);
}
else
{
OpenFileByFallback(path);
}
break;
}
case OpenMode.VaultRecent:
{
if (IsFileInVault(path))
{
OpenFileInVault(path);
}
else
{
OpenFileInRecent(path);
}
break;
}
case OpenMode.Recent:
{
OpenFileInRecent(path);
break;
}
}
}

static bool IsFileInVault(string path)
{
// "This constructor does not check if a directory exists."
var directory = new DirectoryInfo(path);
if (directory.Exists)
{
if (Directory.Exists(directory.FullName + @"\.obsidian"))
{
return true;
}
}

directory = directory.Parent;
while (directory is not null)
{
if (Directory.Exists(directory.FullName + @"\.obsidian"))
{
return true;
}
directory = directory.Parent;
}
return false;
}

static void OpenFileInVault(string path)
{
// WebUtility.UrlEncode() replaces space with "+" instead of "%20"
// Uri.EscapeUriString() replaces space with "%20" but does not replace most reserved characters (!#$&'()+,;=@[])
Process.Start($"obsidian://open?path={Uri.EscapeDataString(path)}");
}

static void OpenFileByFallback(string path)
{
// Process.Start() will not expand environment variables
string editor = Environment.ExpandEnvironmentVariables(_settings.FallbackMarkdownEditor);

// the filename and arguments must be provided seperately when calling Process.Start()
Process.Start(editor, String.Format(_settings.FallbackMarkdownEditorArguments, $@"""{path}"""));
}

static void OpenFileInRecent(string path)
{
// hard link stays valid when the source file is deleted;
// symbolic link requires SeCreateSymbolicLinkPrivilege;
// so we use junction for simplicity

string path_in_recent;

var directory = new DirectoryInfo(path);
if (directory.Exists)
{
// find the Markdown file with the minimum edit distance from the directory name
Fastenshtein.Levenshtein edit_distance = new Fastenshtein.Levenshtein(directory.Name);
int min_distance = int.MaxValue;
FileInfo most_similar_file = null;
foreach (FileInfo file in directory.EnumerateFiles("*.md"))
{
int distance = edit_distance.DistanceFrom(file.Name);
if (distance < min_distance)
{
most_similar_file = file;
min_distance = distance;
}
}

path_in_recent = CreateLinkInRecent(directory, true);

if (most_similar_file is null is false)
path_in_recent += '\\' + most_similar_file.Name;
}
else
{
path_in_recent = CreateLinkInRecent(directory.Parent, false) + '\\' + directory.Name;
}

OpenFileInVault(path_in_recent);
}

static string FormatLinkName(string prefixed_name, bool explicitDirectory)
{
string s = "";

if (prefixed_name[0] == '.')
s += ' ';

s += prefixed_name.Replace('\\', '\');

if (explicitDirectory)
s += "";

return s;
}

static string CreateLinkInRecent(DirectoryInfo directory, bool explicitDirectory)
{
var provider = ReparsePointFactory.Provider;
DirectoryInfo recent = new DirectoryInfo(_settings.RecentVault);

string dir_target_same = null;
List<string> conflict_dirs = new List<string>();
List<string> conflict_targets = new List<string>();
bool TestTarget(string path)
{
ReparseLink link = provider.GetLink(path);
if (link.Target is null)
return false;
/*
if (!Directory.Exists(link.Target))
{
Directory.Delete(path);
return false;
}
*/

if (path.EndsWith($"\\{directory.Name}") || path.EndsWith($"{directory.Name}"))
{
if (link.Target == directory.FullName)
{
dir_target_same = path;
return true;
}

if (!Directory.Exists(link.Target))
{
Directory.Delete(path);
return false;
}

conflict_dirs.Add(path);
conflict_targets.Add(link.Target);
}
else
{
// If the parent directory of a directory is already indexed by Obsidian, the directory won't be indexed before restarting Obsidian.
// The same is true even for a directory whose subdirectories are already indexed.

if (directory.FullName.StartsWith(link.Target)) {
if (path.EndsWith(""))
{
dir_target_same = path + directory.FullName.Substring(link.Target.Length);
return true;
}
else
{
Directory.Delete(path);
return false;
}
}

if (link.Target.StartsWith(directory.FullName))
{
Directory.Delete(path);
return false;
}
}
return false;
}
string formatted_name = FormatLinkName(directory.Name, false);
if (Directory.Exists(recent.FullName + $@"\{formatted_name}") && TestTarget(recent.FullName + $@"\{formatted_name}"))
return dir_target_same;
string formatted_name_explicit = FormatLinkName(directory.Name, true);
if (Directory.Exists(recent.FullName + $@"\{formatted_name_explicit}") && TestTarget(recent.FullName + $@"\{formatted_name_explicit}"))
return dir_target_same;
foreach (DirectoryInfo dir in recent.EnumerateDirectories()) // $"*\{directory.Name}"
{
// reduce unnecessary IO operations
if (dir.Name == ".obsidian" || dir.Name == formatted_name || dir.Name == formatted_name_explicit)
continue;

if (TestTarget(dir.FullName))
return dir_target_same;
}

conflict_targets.Add(directory.FullName);
List<string> prefixed_dirs = PathPrefix.PathPrefixed(conflict_targets);
string path_in_recent = recent.FullName + '\\';
void Move(string source, string dest)
{
if (source == dest)
return;
Directory.Move(source, dest);
}
for (int i = 0; i < conflict_dirs.Count; i++)
{
// may conflict?
Move(conflict_dirs[i], path_in_recent + FormatLinkName(prefixed_dirs[i], conflict_dirs[i].EndsWith("")));
/*
Directory.Delete(conflict_dirs[i]);
provider.CreateLink(path_in_recent + prefixed_dirs[i].Replace('\\', '\'), conflict_targets[i], LinkType.Junction);
*/
}

// Recent = dirs + .obsidian
if (recent.GetDirectories().Length > _settings.RecentVaultSubdirectoriesLimit)
{
(from f in recent.GetDirectories()
orderby f.LastWriteTime ascending
where f.Name != ".obsidian"
select f).First().Delete();
}
_settings = Settings.Load();

path_in_recent += FormatLinkName(prefixed_dirs.Last(), explicitDirectory);
provider.CreateLink(path_in_recent, directory.FullName, LinkType.Junction);
return path_in_recent;
Obsidian obsidian = new Obsidian(_settings);
obsidian.OpenFile(args[0]);
}
}
}
5 changes: 0 additions & 5 deletions ObsidianShell.CLI/packages.config

This file was deleted.

14 changes: 13 additions & 1 deletion ObsidianShell.GUI/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ internal class MainViewModel : INotifyPropertyChanged

public ObservableCollection<OpenModeView> OpenModeViews { get; }

public OpenModeView CurrentOpenModeView {
public OpenModeView CurrentOpenModeView
{
get => OpenModeViews[(int)Settings.OpenMode];
set => Settings.OpenMode = value.OpenMode;
}

public ObsidianOpenMode[] ObsidianOpenModes { get; }

public MainViewModel()
{
Settings = Settings.Load();
Expand Down Expand Up @@ -57,6 +60,15 @@ public MainViewModel()
EnableRecentVault = true
}
};

ObsidianOpenModes = new[]
{
ObsidianOpenMode.CurrentTab,
ObsidianOpenMode.NewTab,
ObsidianOpenMode.NewWindow,
ObsidianOpenMode.NewPane,
ObsidianOpenMode.HoverPopover
};
}

public void Apply()
Expand Down
Loading

0 comments on commit 6f8f30b

Please sign in to comment.