Skip to content

Commit

Permalink
Implement support for multiple languages (#49)
Browse files Browse the repository at this point in the history
* Implement generic textview supporting multi lang
   This is using an EditorContext that lets you implement psedit for a lang
    It can do custom syntax highlighting, formatting, error handling etc.

* Add JSON support for psedit
    We support syntax highlight, formatting and errors

* Move helper functions for editor to extensions
    Moved GetLine, SetCol, StringToRunes and ToRunes to Extensions
    This is to be able to call the methods from  EditorContext

* Set default language when creating new file
* Fixed inconsistent behavior with modified flag + Unsaved
* Fix syntax highlighting when scrolling left/right
* Aligned syntax highlighting in Statusbar with coloring, depending on top/right cursor placement
* Fix so Errors / Syntax Errors dialogs can only be opened by supported contexts
* Reset modified flag when editor text is reverted to original text
* Add string extension to parse Newtonsoft error msg
  • Loading branch information
halvarsson committed Apr 29, 2024
1 parent 6a0e98e commit e4b9211
Show file tree
Hide file tree
Showing 13 changed files with 1,426 additions and 728 deletions.
142 changes: 142 additions & 0 deletions Autocomplete/PowershellAutocomplete.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using Terminal.Gui;

namespace psedit
{
public class PowershellAutocomplete : Autocomplete
{
private readonly Runspace _runspace;

public PowershellAutocomplete(Runspace runspace)
{
_runspace = runspace;
}

private IEnumerable<string> _suggestions;

public void Force()
{
var host = (EditorTextView)HostControl;
var offset = 0;

for (var lineOffset = 0; lineOffset <= host.CurrentRow; lineOffset++)
{
if (lineOffset == host.CurrentRow)
{
offset += host.CurrentColumn;
}
else
{
offset += host.Runes[lineOffset].Count + Environment.NewLine.Length;
}
}

using (var powerShell = PowerShell.Create())
{
powerShell.Runspace = _runspace;
var results = CommandCompletion.CompleteInput(host.Text.ToString(), offset, new Hashtable(), powerShell);
Suggestions = results.CompletionMatches.Select(m => m.CompletionText).ToList().AsReadOnly();
_suggestions = Suggestions;
}
}

private void TryGenerateSuggestions()
{
var host = (EditorTextView)HostControl;
var offset = 0;

for (var lineOffset = 0; lineOffset <= host.CurrentRow; lineOffset++)
{
if (lineOffset == host.CurrentRow)
{
offset += host.CurrentColumn;
}
else
{
offset += host.Runes[lineOffset].Count + Environment.NewLine.Length;
}
}
var text = host.Text.ToString();

if (text.Length == 0 || offset == 0 || host.CurrentColumn == 0)
{
ClearSuggestions();
return;
}

var currentChar = text[offset - 1];

if (currentChar != '-' &&
currentChar != ':' &&
currentChar != '.' &&
currentChar != '.' &&
currentChar != '\\' &&
currentChar != '$')
{
if (_suggestions != null)
{
var word = GetCurrentWord();
if (!System.String.IsNullOrEmpty(word))
{
Suggestions = _suggestions.Where(m => m.StartsWith(word, StringComparison.OrdinalIgnoreCase)).ToList().AsReadOnly();
}
else
{
ClearSuggestions();
}
}

return;
}

using (var powerShell = PowerShell.Create())
{
powerShell.Runspace = _runspace;
var results = CommandCompletion.CompleteInput(host.Text.ToString(), offset, new Hashtable(), powerShell);
Suggestions = results.CompletionMatches.Select(m => m.CompletionText).ToList().AsReadOnly();
_suggestions = Suggestions;
}
}

public override void GenerateSuggestions()
{
try
{
TryGenerateSuggestions();
}
catch { }
}

public override bool IsWordChar(Rune rune)
{
var c = (char)rune;
return Char.IsLetterOrDigit(c) || c == '$' || c == '-';
}

///<inheritdoc/>
protected override string GetCurrentWord()
{
var host = (TextView)HostControl;
var currentLine = host.GetCurrentLine();
var cursorPosition = Math.Min(host.CurrentColumn, currentLine.Count);
return IdxToWord(currentLine, cursorPosition);
}

/// <inheritdoc/>
protected override void DeleteTextBackwards()
{
((TextView)HostControl).DeleteCharLeft();
}

/// <inheritdoc/>
protected override void InsertText(string accepted)
{
((TextView)HostControl).InsertText(accepted);
}
}
}
20 changes: 12 additions & 8 deletions Editor/SyntaxErrorDialog.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Management.Automation;
using System.Management.Automation.Language;
using System.Management.Automation.Runspaces;
using System.Text;
using Terminal.Gui;

namespace psedit
{
internal class SyntaxErrorDialog
{
public static void Show(ParseError[] errors)
public static void Show(ConcurrentDictionary<Point, string> errors)
{
var dataTable = new DataTable();
dataTable.Columns.Add("Line");
dataTable.Columns.Add("Column");
dataTable.Columns.Add("Message");

// sort errors by line / column
List<Point> sortedList = new List<Point>(errors.Keys);
sortedList.Sort((x,y) => {
var ret = x.Y.CompareTo(y.Y);
if (ret == 0) ret = x.X.CompareTo(y.X);
return ret;
});

foreach (var error in errors)
foreach (var error in sortedList)
{
dataTable.Rows.Add(error.Extent.StartLineNumber, error.Extent.StartColumnNumber, error.Message);

dataTable.Rows.Add(error.Y, error.X, errors[error]);
}

var tableView = new TableView(dataTable);
Expand Down
53 changes: 53 additions & 0 deletions EditorContext/EditorContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using Terminal.Gui;

namespace psedit
{
public abstract class EditorContext
{
public string _originalText;
public int _lastParseTopRow;
public int _lastParseRightColumn;
public int _tabWidth;
public bool CanFormat = false;
public bool CanAutocomplete = false;
public bool CanRun = false;
public bool CanSyntaxHighlight = false;
public ConcurrentDictionary<Point, string> Errors = new ConcurrentDictionary<Point, string>();
public ConcurrentDictionary<Point, string> ColumnErrors = new ConcurrentDictionary<Point, string>();
public Dictionary<Point, Terminal.Gui.Color> pointColorDict = new Dictionary<Point, Terminal.Gui.Color>();
public abstract void ParseText(int height, int topRow, int left, int right, string text, List<List<Rune>> Runes);
public virtual string Format(string text)
{
throw new NotImplementedException();
}
public virtual string Run(string path)
{
throw new NotImplementedException();
}
public virtual string RunText(string text)
{
throw new NotImplementedException();
}
public virtual void RunCurrentRunspace(string path)
{
throw new NotImplementedException();
}
public virtual void RunTextCurrentRunspace(string text)
{
throw new NotImplementedException();
}

public Color GetColorByPoint(Point point)
{
Color returnColor = Color.Green;
if (pointColorDict.ContainsKey(point))
{
returnColor = pointColorDict[point];
}
return returnColor;
}
}
}
Loading

0 comments on commit e4b9211

Please sign in to comment.