Syntax highlighting + command palette

This commit is contained in:
Trevi Awater
2024-03-02 21:22:29 +01:00
parent eb7154d897
commit cd30e482dd
17 changed files with 197 additions and 6 deletions

View File

@@ -7,7 +7,11 @@
// © Xamarin.Neo4j.iOS // © Xamarin.Neo4j.iOS
// //
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using CoreGraphics; using CoreGraphics;
using Foundation;
using UIKit; using UIKit;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS; using Xamarin.Forms.Platform.iOS;
@@ -19,12 +23,52 @@ namespace Xamarin.Neo4j.iOS.CustomRenderers
{ {
public class QueryEditorRenderer : EditorRenderer public class QueryEditorRenderer : EditorRenderer
{ {
private readonly string[] _keyWords =
{
// Clauses
"CALL", "CREATE", "DELETE", "DETACH", "FOREACH", "LOAD", "MATCH", "MERGE", "OPTIONAL", "REMOVE", "RETURN", "SET", "START", "UNION", "UNWIND", "WITH",
// Subclauses
"LIMIT", "ORDER", "SKIP", "WHERE", "YIELD",
// Modifiers
"ASC", "ASCENDING", "ASSERT", "BY", "CSV", "DESC", "DESCENDING", "ON",
// Expressions
"ALL", "CASE", "COUNT", "ELSE", "END", "EXISTS", "THEN", "WHEN",
// Operators
"AND", "AS", "CONTAINS", "DISTINCT", "ENDS", "IN", "IS", "NOT", "OR", "STARTS", "XOR",
// Schema
"CONSTRAINT", "CREATE", "DROP", "EXISTS", "INDEX", "NODE", "KEY", "UNIQUE",
// Hints
"INDEX", "JOIN", "SCAN", "USING",
// Literals
"FALSE", "NULL", "TRUE"
};
protected override void OnElementChanged(ElementChangedEventArgs<Editor> e) protected override void OnElementChanged(ElementChangedEventArgs<Editor> e)
{ {
base.OnElementChanged(e); base.OnElementChanged(e);
if (e.NewElement != null)
{
Element.TextChanged += (sender, args) =>
{
HighlightWords(_keyWords);
};
}
if (Control != null && Element != null) if (Control != null && Element != null)
{ {
Control.AutocorrectionType = UITextAutocorrectionType.No;
Control.AutocapitalizationType = UITextAutocapitalizationType.None;
Control.SpellCheckingType = UITextSpellCheckingType.No;
Control.KeyboardType = UIKeyboardType.Default;
var executeButton = new UIBarButtonItem("Execute", UIBarButtonItemStyle.Done, (sender, args) => var executeButton = new UIBarButtonItem("Execute", UIBarButtonItemStyle.Done, (sender, args) =>
{ {
if (Element is QueryEditor queryEditor) if (Element is QueryEditor queryEditor)
@@ -35,15 +79,85 @@ namespace Xamarin.Neo4j.iOS.CustomRenderers
} }
}); });
var toolbar = new UIToolbar(new CGRect(0.0f, 0.0f, Control.Frame.Size.Width, 44.0f)); var keyButtons = new[]
toolbar.Items = new[]
{ {
new UIBarButtonItem("(", UIBarButtonItemStyle.Plain, (sender, args) => InsertText("(")),
new UIBarButtonItem(")", UIBarButtonItemStyle.Plain, (sender, args) => InsertText(")")),
new UIBarButtonItem("[", UIBarButtonItemStyle.Plain, (sender, args) => InsertText("[")),
new UIBarButtonItem("]", UIBarButtonItemStyle.Plain, (sender, args) => InsertText("]")),
new UIBarButtonItem(":", UIBarButtonItemStyle.Plain, (sender, args) => InsertText(":")),
new UIBarButtonItem("-", UIBarButtonItemStyle.Plain, (sender, args) => InsertText("-")),
new UIBarButtonItem("\u2192", UIBarButtonItemStyle.Plain, (sender, args) => InsertText("->")),
new UIBarButtonItem("\u2190", UIBarButtonItemStyle.Plain, (sender, args) => InsertText("<-")),
new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace), new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace),
executeButton executeButton
}; };
var toolbar = new UIToolbar(new CGRect(0.0f, 0.0f, Control.Frame.Size.Width, 44.0f));
toolbar.Items = keyButtons;
Control.InputAccessoryView = toolbar; Control.InputAccessoryView = toolbar;
} }
} }
private void HighlightWords(IEnumerable<string> wordsToHighlight)
{
var text = PreprocessText(Control.Text);
var attributedText = new NSMutableAttributedString(text);
// Iterate through the array and apply formatting to the editor's text.
foreach (var word in wordsToHighlight)
{
var regex = new Regex("\\b" + Regex.Escape(word) + "\\b", RegexOptions.IgnoreCase);
foreach (Match match in regex.Matches(text))
{
attributedText.AddAttribute(UIStringAttributeKey.ForegroundColor, Color.FromHex("#89982e").ToUIColor(),
new NSRange(match.Index, match.Length));
}
}
// Apply text color formatting for text inside single quotes.
ApplyQuoteTextColorFormatting(text, attributedText, "'(.*?)'", Color.FromHex("#ae8b2d").ToUIColor());
// Apply text color formatting for text inside double quotes.
ApplyQuoteTextColorFormatting(text, attributedText, "\"(.*?)\"", Color.FromHex("#ae8b2d").ToUIColor());
attributedText.AddAttribute(UIStringAttributeKey.Font, UIFont.SystemFontOfSize((nfloat) Element.FontSize), new NSRange(0, attributedText.Length));
Control.AttributedText = attributedText;
}
private void InsertText(string text)
{
Control.InsertText(text);
}
private static void ApplyQuoteTextColorFormatting(string text, NSMutableAttributedString attributedText,
string quotePattern, UIColor color)
{
var quoteRegex = new Regex(quotePattern);
foreach (Match match in quoteRegex.Matches(text))
{
attributedText.AddAttribute(UIStringAttributeKey.ForegroundColor, color,
new NSRange(match.Index, match.Length));
}
}
private static string PreprocessText(string text)
{
// Replace single quotation marks and their curly variants with: '
text = text.Replace("", "'");
text = text.Replace("", "'");
// Replace double quotation marks and their curly variants with: "
text = text.Replace("“", "\"");
text = text.Replace("”", "\"");
return text;
}
} }
} }

View File

@@ -27,7 +27,9 @@
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>nl.resoftware.pocketgraph</string> <string>nl.resoftware.pocketgraph</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0.3</string> <string>4</string>
<key>CFBundleShortVersionString</key>
<string>1.1.0</string>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>CFBundleName</key> <key>CFBundleName</key>

View File

@@ -1,6 +1,7 @@
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Xamarin.Forms;
// General Information about an assembly is controlled through the following // General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
@@ -26,4 +27,6 @@ using System.Runtime.InteropServices;
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ExportFont("RobotoMono-Regular.ttf", Alias = "RobotoMonoRegular")]

View File

@@ -11,6 +11,7 @@ using UIKit;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Neo4j.iOS.Services; using Xamarin.Neo4j.iOS.Services;
using Xamarin.Neo4j.Services; using Xamarin.Neo4j.Services;
using Xamarin.Neo4j.Services.Interfaces;
[assembly: Dependency(typeof(ScreenSizeService))] [assembly: Dependency(typeof(ScreenSizeService))]
namespace Xamarin.Neo4j.iOS.Services namespace Xamarin.Neo4j.iOS.Services

View File

@@ -11,6 +11,7 @@ using Neo4j.Driver;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Neo4j.iOS.Services; using Xamarin.Neo4j.iOS.Services;
using Xamarin.Neo4j.Services; using Xamarin.Neo4j.Services;
using Xamarin.Neo4j.Services.Interfaces;
[assembly: Dependency(typeof(TrustManagerService))] [assembly: Dependency(typeof(TrustManagerService))]
namespace Xamarin.Neo4j.iOS.Services namespace Xamarin.Neo4j.iOS.Services

View File

@@ -0,0 +1,24 @@
//
// VersionService.cs
//
// Trevi Awater
// 02-03-2024
//
// © Xamarin.Neo4j.iOS
//
using Foundation;
using Xamarin.Forms;
using Xamarin.Neo4j.iOS.Services;
using Xamarin.Neo4j.Services.Interfaces;
[assembly: Dependency(typeof(VersionService))]
namespace Xamarin.Neo4j.iOS.Services
{
public class VersionService : IVersionService
{
public string GetVersion() => NSBundle.MainBundle.InfoDictionary["CFBundleShortVersionString"]?.ToString();
public string GetBuild() => NSBundle.MainBundle.InfoDictionary["CFBundleVersion"]?.ToString();
}
}

View File

@@ -100,6 +100,7 @@
<Compile Include="Security\NativeTrustManager.cs" /> <Compile Include="Security\NativeTrustManager.cs" />
<Compile Include="Services\ScreenSizeService.cs" /> <Compile Include="Services\ScreenSizeService.cs" />
<Compile Include="Services\TrustManagerService.cs" /> <Compile Include="Services\TrustManagerService.cs" />
<Compile Include="Services\VersionService.cs" />
<None Include="Entitlements.plist" /> <None Include="Entitlements.plist" />
<None Include="Info.plist" /> <None Include="Info.plist" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

View File

@@ -18,6 +18,11 @@ namespace Xamarin.Neo4j.Controls
public event EventHandler ExecuteClicked; public event EventHandler ExecuteClicked;
public QueryEditor()
{
FontFamily = "RobotoMonoRegular";
}
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint) protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{ {
var sizeRequest = base.OnMeasure(widthConstraint, heightConstraint); var sizeRequest = base.OnMeasure(widthConstraint, heightConstraint);

View File

@@ -17,6 +17,7 @@
<TableView VerticalOptions="StartAndExpand" Intent="Settings" Background="Transparent"> <TableView VerticalOptions="StartAndExpand" Intent="Settings" Background="Transparent">
<TableRoot> <TableRoot>
<TableSection> <TableSection>
<TextCell Text="{Binding VersionLabel}" />
<TextCell Text="Software Licenses" Command="{Binding Commands[OpenLicensesPage]}" /> <TextCell Text="Software Licenses" Command="{Binding Commands[OpenLicensesPage]}" />
</TableSection> </TableSection>
</TableRoot> </TableRoot>

View File

@@ -1,4 +1,4 @@
namespace Xamarin.Neo4j.Services namespace Xamarin.Neo4j.Services.Interfaces
{ {
public interface IScreenSizeService public interface IScreenSizeService
{ {

View File

@@ -1,6 +1,6 @@
using Neo4j.Driver; using Neo4j.Driver;
namespace Xamarin.Neo4j.Services namespace Xamarin.Neo4j.Services.Interfaces
{ {
public interface ITrustManagerService public interface ITrustManagerService
{ {

View File

@@ -0,0 +1,9 @@
namespace Xamarin.Neo4j.Services.Interfaces
{
public interface IVersionService
{
string GetVersion();
string GetBuild();
}
}

View File

@@ -16,6 +16,7 @@ using Neo4jClient.Cypher;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Neo4j.Models; using Xamarin.Neo4j.Models;
using Xamarin.Neo4j.Services; using Xamarin.Neo4j.Services;
using Xamarin.Neo4j.Services.Interfaces;
using Xamarin.Neo4j.Utilities; using Xamarin.Neo4j.Utilities;
[assembly: Dependency(typeof(Neo4jService))] [assembly: Dependency(typeof(Neo4jService))]

View File

@@ -22,6 +22,7 @@ using Xamarin.Neo4j.Annotations;
using Xamarin.Neo4j.Models; using Xamarin.Neo4j.Models;
using Xamarin.Neo4j.Pages; using Xamarin.Neo4j.Pages;
using Xamarin.Neo4j.Services; using Xamarin.Neo4j.Services;
using Xamarin.Neo4j.Services.Interfaces;
using Xamarin.Neo4j.Utilities; using Xamarin.Neo4j.Utilities;
namespace Xamarin.Neo4j.ViewModels namespace Xamarin.Neo4j.ViewModels

View File

@@ -14,12 +14,17 @@ using Xamarin.Essentials;
using Xamarin.Forms; using Xamarin.Forms;
using Xamarin.Neo4j.Annotations; using Xamarin.Neo4j.Annotations;
using Xamarin.Neo4j.Pages; using Xamarin.Neo4j.Pages;
using Xamarin.Neo4j.Services.Interfaces;
namespace Xamarin.Neo4j.ViewModels namespace Xamarin.Neo4j.ViewModels
{ {
public class SettingsViewModel : ViewModelBase, INotifyPropertyChanged public class SettingsViewModel : ViewModelBase, INotifyPropertyChanged
{ {
private string _versionLabel { get; set; }
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
private IVersionService _versionService { get; set; }
public SettingsViewModel(INavigation navigation) : base(navigation) public SettingsViewModel(INavigation navigation) : base(navigation)
{ {
@@ -32,6 +37,10 @@ namespace Xamarin.Neo4j.ViewModels
{ {
await Navigation.PushAsync(new LicensesPage()); await Navigation.PushAsync(new LicensesPage());
})); }));
_versionService = DependencyService.Get<IVersionService>();
VersionLabel = $"Version: {_versionService.GetVersion()} (Build: {_versionService.GetBuild()})";
} }
[NotifyPropertyChangedInvocator] [NotifyPropertyChangedInvocator]
@@ -39,5 +48,22 @@ namespace Xamarin.Neo4j.ViewModels
{ {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
} }
#region Bindable Properties
public string VersionLabel
{
get => _versionLabel;
set
{
_versionLabel = value;
OnPropertyChanged(nameof(VersionLabel));
}
}
#endregion
} }
} }

View File

@@ -23,5 +23,7 @@
<EmbeddedResource Include="Visualization\neovis.html" /> <EmbeddedResource Include="Visualization\neovis.html" />
<None Remove="Resources\licenses.json" /> <None Remove="Resources\licenses.json" />
<EmbeddedResource Include="Resources\licenses.json" /> <EmbeddedResource Include="Resources\licenses.json" />
<None Remove="Assets\Fonts\RobotoMono-Regular.ttf" />
<EmbeddedResource Include="Assets\Fonts\RobotoMono-Regular.ttf" />
</ItemGroup> </ItemGroup>
</Project> </Project>