Vamos a programar #68 - Syntax Highlighter M (beta 2)

Hola de nuevo a todos, el dia de hoy vamos a continuar con mas de  Syntax Highlighter M ahora en su versión beta 2.


En el post anterior vimos cómo implementar el programa de tal forma que coloreaba el código en C#, pero la idea del programa es poder resaltar la sintaxis de cualquier programa. Hay dos posibilidades para lograr esa tarea; la primera: programar cada expresión regular para cada lenguaje dentro del programa, hacerlo tantas veces cómo lenguajes haya; la segunda: crear archivos externos que contengan un patrón para cada lenguaje y cargarlo solo cuando sea necesario.

La segunda forma me parece la mas optima, ya que se puede extender la funcionalidad del programa de forma relativamente más rápida que teniendo que compilar el programa cada vez que se quiera agregar un nuevo lenguaje. Para poder almacenar los patrones, decidí usar archivos XML, ya que ofrecen una manera sencilla de almacenar la información y .NET ayuda a manejar estos archivos de manera sencilla.

La aplicación ahora consta de tres partes, dos formularios y una clase, de manera correspondiente, los códigos fuente son los siguientes:

/*Iconos cortesia de:
//https://www.flaticon.com/		
//y
//https://www.flaticon.com/authors/dave-gandy
*/
using System;
using System.Drawing;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using System.Xml;

namespace SyntaxHighlighter
{
	public partial class FrmMain : Form
	{

		#region Cargar Estilos por default
		private Color FontColor1 = (Color)Properties.Settings.Default["ColorRW1"];
		private Color FontColor2 = (Color)Properties.Settings.Default["ColorRW2"];
		private Color FontColor3 = (Color)Properties.Settings.Default["ColorRW3"];
		private Color ColorComments = (Color)Properties.Settings.Default["ColorCom"];
		private Color ColorNum = (Color)Properties.Settings.Default["ColorNum"];
		private Color ColorStrings = (Color)Properties.Settings.Default["ColorStrings"];
		private Color ColorOperator = (Color)Properties.Settings.Default["ColorOp"];
		private Color GeneralColor = (Color)Properties.Settings.Default["GeneralFontColor"];
		private Font GeneralFont = (Font)Properties.Settings.Default["GeneralFont"];
		private string CurrentLang = "Texto plano";
		#endregion

		public string Comments = "";
		public string Numbers = "";
		public string Strings = "";
		public string Operators = "";
		public string ReservedWords3 = "";
		public string ReservedWords2 = "";
		public string ReservedWords1 = "";

		#region Delegados
		private delegate void UpadteRTBDelegate(int Start, int End, Color Colores);
		private delegate string GetTextDelegate();
		#endregion

		private void UpdateRTB(int Start, int End, Color Colores)
		{
			if (RTBMain.InvokeRequired)
				RTBMain.Invoke(new UpadteRTBDelegate(UpdateRTB), Start, End, Colores);
			else
			{
				RTBMain.SelectionStart = Start;
				RTBMain.SelectionLength = End;
				RTBMain.SelectionColor = Colores;
			}

		}

		private string GetText()
		{
			if (RTBMain.InvokeRequired)
				return (string)RTBMain.Invoke(new GetTextDelegate(GetText));
			else
				return RTBMain.Text;
		}

		private void DoTheWork()
		{
			Thread DoWork = new Thread(new ThreadStart(() => ColorizeAll()));
			DoWork.Start();
		}

		private void LoadProfile(string InXMLFile)
		{
			using (XmlReader XMLR = new XmlTextReader(InXMLFile))
			{
				try
				{
					XMLR.ReadToFollowing("Perfil");
					XMLR.ReadToFollowing("Lenguaje");
					CurrentLang = XMLR.ReadElementContentAsString();
					XMLR.ReadToFollowing("RW1");
					ReservedWords1 = XMLR.ReadElementContentAsString();
					XMLR.ReadToFollowing("RW2");
					ReservedWords2 = XMLR.ReadElementContentAsString();
					XMLR.ReadToFollowing("RW3");
					ReservedWords3 = XMLR.ReadElementContentAsString();
					XMLR.ReadToFollowing("Comentarios");
					Comments = XMLR.ReadElementContentAsString();
					XMLR.ReadToFollowing("Numeros");
					Numbers = XMLR.ReadElementContentAsString();
					XMLR.ReadToFollowing("Texto");
					Strings = XMLR.ReadElementContentAsString();
					XMLR.ReadToFollowing("Operadores");
					Operators = XMLR.ReadElementContentAsString();
					TSSLblLang.Text = CurrentLang;
				}
				catch (Exception)
				{

					MessageBox.Show("Parece que el XML no es valido", "Syntax Highlighter M (beta)", MessageBoxButtons.OK, MessageBoxIcon.Error);
				}

			}
		}

		public FrmMain()
		{
			InitializeComponent();
		}

		private void ColorizeAll()
		{
			Colorize(ReservedWords1, WordTypes.WType1);
			Colorize(ReservedWords2, WordTypes.WType2);
			Colorize(ReservedWords3, WordTypes.WType3);
			Colorize(Numbers, WordTypes.NType);
			Colorize(Operators, WordTypes.OpType);
			Colorize(Strings, WordTypes.SType);
			Colorize(Comments, WordTypes.CType);
		}

		private void SaveColor(WordTypes Tipo, Color Colores)
		{
			switch (Tipo)
			{
				case WordTypes.WType1:
					FontColor1 = Colores;
					Properties.Settings.Default["ColorRW1"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.WType2:
					FontColor2 = Colores;
					Properties.Settings.Default["ColorRW2"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.WType3:
					FontColor3 = Colores;
					Properties.Settings.Default["ColorRW3"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.NType:
					ColorNum = Colores;
					Properties.Settings.Default["ColorNum"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.OpType:
					ColorOperator = Colores;
					Properties.Settings.Default["ColorOp"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.SType:
					ColorStrings = Colores;
					Properties.Settings.Default["ColorStrings"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.CType:
					ColorComments = Colores;
					Properties.Settings.Default["ColorCom"] = Colores;
					Properties.Settings.Default.Save();
					break;
				case WordTypes.OtherType:
					GeneralColor = Colores;
					RTBMain.ForeColor = GeneralColor;
					Properties.Settings.Default["GeneralFontColor"] = Colores;
					Properties.Settings.Default.Save();
					break;
				default:
					break;
			}
		}

		private void Colorize(string Patron, WordTypes Tipo)
		{
			Color CurrentFontColor = GeneralColor;
			switch (Tipo)
			{
				case WordTypes.WType1:
					CurrentFontColor = FontColor1;
					break;
				case WordTypes.WType2:
					CurrentFontColor = FontColor2;
					break;
				case WordTypes.WType3:
					CurrentFontColor = FontColor3;
					break;
				case WordTypes.CType:
					CurrentFontColor = ColorComments;
					break;
				case WordTypes.NType:
					CurrentFontColor = ColorNum;
					break;
				case WordTypes.SType:
					CurrentFontColor = ColorStrings;
					break;
				case WordTypes.OpType:
					CurrentFontColor = ColorOperator;
					break;
				case WordTypes.OtherType:
					CurrentFontColor = GeneralColor;
					break;
			}
			try
			{
				//Si quitas el comentario siguiente y comentas la linea que sigue, puedes ver lo importante de la funcion GetText()
				//MatchCollection wordColl = Regex.Matches(RTBMain.Text, Patron);
				MatchCollection wordColl = Regex.Matches(GetText(), Patron);
				foreach (Match m in wordColl)
				{
					UpdateRTB(m.Index, m.Length, CurrentFontColor);
				}
			}
			catch
			{
			}
		}

		private void TSBtnOpen_Click(object sender, EventArgs e)
		{
			using (OpenFileDialog OpDiag = new OpenFileDialog())
			{
				OpDiag.Filter = "Todos los archivos|*.*|Código fuente C#|*.cs|Arvhivos de texto|*.txt";
				OpDiag.Multiselect = false;
				DialogResult DiagRes = OpDiag.ShowDialog();
				if (DiagRes == DialogResult.OK)
				{
					RTBMain.Clear();
					StreamReader SR = new StreamReader(OpDiag.FileName, Encoding.UTF8);
					RTBMain.AppendText(SR.ReadToEnd());
					DoTheWork();

				}
			}
		}

		private void TSBtnSave_Click(object sender, EventArgs e)
		{
			using (SaveFileDialog SaveDiag = new SaveFileDialog())
			{
				SaveDiag.Filter = "Texto enriquecido|*.rtf";
				if (SaveDiag.ShowDialog() == DialogResult.OK)
					RTBMain.SaveFile(SaveDiag.FileName);
			}
		}
		private void TSBtnFont_Click(object sender, EventArgs e)
		{
			using (FontDialog FontDiag = new FontDialog())
			{
				FontDiag.ShowApply = false;
				if (FontDiag.ShowDialog() == DialogResult.OK)
				{
					RTBMain.Font = FontDiag.Font;
					Properties.Settings.Default["GeneralFont"] = FontDiag.Font;
					Properties.Settings.Default.Save();
					DoTheWork();
				}
			}
		}

		private void TSBtnColor_Click(object sender, EventArgs e)
		{
			using (ColorDialog ColorDiag = new ColorDialog())
			{
				if (ColorDiag.ShowDialog() == DialogResult.OK)
				{
					SaveColor((WordTypes)TSLBWordTypes.SelectedIndex, ColorDiag.Color);
				}

			}
		}

		private void FrmMain_Load(object sender, EventArgs e)
		{
			RTBMain.Font = GeneralFont;
			RTBMain.ForeColor = GeneralColor;
			TSLBWordTypes.SelectedIndex = 7;
			TSSLblLang.Text = CurrentLang;
		}

		private void TSBtnReDo_Click(object sender, EventArgs e)
		{
			DoTheWork();
		}

		private void TSLBWordTypes_TextChanged(object sender, EventArgs e)
		{

		}

		private void TSBtnSaveLangStyle_Click(object sender, EventArgs e)
		{
			using (Form2 FrmOptions = new Form2())
			{

				FrmOptions.ShowDialog();

			}
		}

		private void TSLoadLang_Click(object sender, EventArgs e)
		{
			using (OpenFileDialog OpDiag = new OpenFileDialog())
			{
				OpDiag.Filter = "Archivos XML|*.xml";
				if (OpDiag.ShowDialog() == DialogResult.OK)
				{
					LoadProfile(OpDiag.FileName);
				}
			}
		}
	}
}

El código del formulario 2

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Xml;

namespace SyntaxHighlighter
{
	public partial class Form2 : Form
	{
		private string CurrentLang = "Texto plano";
		private string Comments = "";
		private string Numbers = "";
		private string Strings = "";
		private string Operators = "";
		private string ReservedWords3 = "";
		private string ReservedWords2 = "";
		private string ReservedWords1 = "";

		private void SaveRegEx(string Patron, WordTypes Tipo)
		{
			switch (Tipo)
			{
				case WordTypes.WType1:
					ReservedWords1 = Patron;
					break;
				case WordTypes.WType2:
					ReservedWords2 = Patron;
					break;
				case WordTypes.WType3:
					ReservedWords3 = Patron;
					break;
				case WordTypes.NType:
					Numbers = Patron;
					break;
				case WordTypes.OpType:
					Operators = Patron;
					break;
				case WordTypes.SType:
					Strings = Patron;
					break;
				case WordTypes.CType:
					Comments = Patron;
					break;
				case WordTypes.OtherType:
					break;
				default:
					break;
			}
		}

		private string GetRegEx(WordTypes Cual)
		{
			switch (Cual)
			{
				case WordTypes.WType1:
					return ReservedWords1;
				case WordTypes.WType2:
					return ReservedWords2;
				case WordTypes.WType3:
					return ReservedWords3;
				case WordTypes.NType:
					return Numbers;
				case WordTypes.OpType:
					return Operators;
				case WordTypes.SType:
					return Strings;
				case WordTypes.CType:
					return Comments;
				case WordTypes.OtherType:
					return "";
				default:
					return "";
			}
		}

		public Form2()
		{
			InitializeComponent();
		}

		private void Form2_Load(object sender, EventArgs e)
		{
			LBWordTypes.SelectedIndex = 0;
			TxtLang.Text = CurrentLang;

		}

		private void BtnSave_Click(object sender, EventArgs e)
		{

			SaveRegEx(TxtExpression.Text, (WordTypes)LBWordTypes.SelectedIndex);
		}

		private void LBWordTypes_SelectedIndexChanged(object sender, EventArgs e)
		{
			TxtExpression.Text = GetRegEx((WordTypes)LBWordTypes.SelectedIndex);
		}

		private void BtnSaveToFile_Click(object sender, EventArgs e)
		{
			CurrentLang = TxtLang.Text;
			using (SaveFileDialog SavDiag = new SaveFileDialog())
			{
				SavDiag.Filter = "Archivo XML|*.xml";
				if (SavDiag.ShowDialog() == DialogResult.OK)
				{
					HandleData HD = new HandleData();
					HD.SaveProfile(SavDiag.FileName, CurrentLang, CurrentLang, ReservedWords1, ReservedWords2, ReservedWords3, Comments, Numbers, Strings, Operators);
				}
			}
		}

		private void TxtExpression_TextChanged(object sender, EventArgs e)
		{
			SaveRegEx(TxtExpression.Text, (WordTypes)LBWordTypes.SelectedIndex);
		}

		private void BtnOpen_Click(object sender, EventArgs e)
		{

		}
	}
}

El código de la clase

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace SyntaxHighlighter
{
	public enum WordTypes : int
	{
		WType1 = 0,
		WType2,
		WType3,
		NType,
		OpType,
		SType,
		CType,
		OtherType
	}
	class HandleData
	{
		public void SaveProfile(string XmlPath, string Lenguaje, string CurrentLang, string ReservedWords1, string ReservedWords2,
			string ReservedWords3, string Comments, string Numbers, string Strings, string Operators)
		{
			XmlWriterSettings settings = new XmlWriterSettings();
			settings.Indent = true;
			settings.IndentChars = "\t";
			using (XmlWriter XWriter = XmlWriter.Create(XmlPath, settings))
			{
				XWriter.WriteStartDocument();
				XWriter.WriteComment("Syntax Highlighter M (beta)");
				XWriter.WriteStartElement("Perfil");
				XWriter.WriteElementString("Lenguaje", CurrentLang);
				XWriter.WriteElementString("RW1", ReservedWords1);
				XWriter.WriteElementString("RW2", ReservedWords2);
				XWriter.WriteElementString("RW3", ReservedWords3);
				XWriter.WriteElementString("Comentarios", Comments);
				XWriter.WriteElementString("Numeros", Numbers);
				XWriter.WriteElementString("Texto", Strings);
				XWriter.WriteElementString("Operadores", Operators);
				XWriter.WriteEndElement();
				XWriter.WriteEndDocument();
			}
		}
	}
}

Cómo verás el código aun no está bien ordenado, pero eso se debe a que todavía van a haber cambios importantes por eso veremos la explicación y la presentación de Syntax Highlighter M 1.0 en el siguiente post, por ahora puedes divertirte averiguando cómo es que funciona (tanto el programa cómo el código). La descarga incluye dos archivos XML, uno de ellos contienen las expresiones regulares para el código de C# y el otro medio tiene la parte para resaltar el código de arduino y lo puedes descargar de mi dropbox cómo ya es costumbre.

Los leo luego.

No hay comentarios.