Xwork's Blog

The lord is waiting to take your hand.

Mostrando las entradas con la etiqueta Syntax Highlighter. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Syntax Highlighter. Mostrar todas las entradas

Vamos a programar #69 - Syntax Highlighter M 1.0

Hola de nuevo a todos, el día de hoy vamos a terminar con el código de Syntax Highlighter M.
En los post anteriores implementamos la mayor parte del código, solo restaba implementar una parte para poder decir que es la versión 1.0 del programa.

El Código.


Primero veremos el código fuente de los dos formularios y la clase, y después explicare cada parte.

El código del formulario 1 es el siguiente:

/*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 codigo del formulario 2 es el siguiente:


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 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();
					TxtLang.Text = CurrentLang;
				}
				catch (Exception)
				{

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

			}
		}
		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)
		{
			using (OpenFileDialog OpDiag = new OpenFileDialog())
			{
				OpDiag.Filter = "Archivos XML|*.xml";
				if (OpDiag.ShowDialog() == DialogResult.OK)
				{
					LoadProfile(OpDiag.FileName);
					LBWordTypes.SelectedIndex = 0;
					TxtExpression.Text = GetRegEx((WordTypes)0);
				}
			}
		}
	}
}

El código de la clase es el siguiente:


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


Primero revisemos el código del formulario 1. Primero definimos todas la variables que almacenarán los datos que están guardados en los ajustes, en los ajustes guardamos todos los estilos, fuentes y algunos datos más que vamos a usar. Luego iniciamos las variables que contendrán las expresiones regulares, se dejan vacías puesto que todas se van a cargar de los archivos externos. De ahi en fuera las funciones permanecen igual.

En el formulario 2, se han añadido tres funciones, la primera de ellas en orden de aparición es "SaveRegEx()" su función solamente es la de asignarle el valor del parámetro "Patron" a la variable indicada dependiendo de cual sea el valor del parámetro "Tipo"; este del tipo "WordTypes".
La siguiente función es "LoadProfile", está función es idéntica a la que se encuentra en el formulario 1, al inicio iba a ponerla en la clase para acceder al ella, pero cómo había que hacer más cambios, simplemente decidí no hacerlo (la pereza es mala ;)) y simplemente copie la función y cree variables con el mismo nombre (después veremos porque si podemos hacer esto).
La siguiente función es "GetRegEx()", esta recibe un parámetro del tipo "WordTypes" llamado "Cual", el objetivo de esta, es obtener el valor de la variable que contienen el valor de una expresión regular, dependiendo el caso regresara un valor con la expresión regular, esto lo usamos para cada vez que se cambie la selección de un elemento de "LBWordTypes" que es un "ListBox", obtengamos el valor adecuado dependiendo de la selección.
En este formulario podemos crear un estilo o editar uno ya existente y precisamente esta era la razón por la cual en el post anterior no podíamos decir que el programa estaba terminado, ahora tenemos la capicidad de editar un estilo existente o de crear uno nuevo.

La clase "HandleData" no contiene cambios. En ella simplemente definimos la enumeración "WordTypes" y creamos la función "SaveProfile" que se usa para guardar las expresiones regulares en un archivo XML, la idea original era poner aquí todas las funciones, pero por cuestiones que son difíciles de explicar (recuerden otra vez, la pereza es mala), simplemente no lo hice.

Con lo anterior hemos creado un programa que sirve para resaltar la sintaxis, la descarga incluye dos archivos XML con las expresiones regulares que sirven para resaltar el código en C# y C para arduino, puedes usarlo cómo ejemplo para crear otros. Solo debes de tomar en cuenta que en lenguajes cómo visual basic las palabras reservadas no importa si están con mayúsculas o minúsculas.

¿Cómo usarlo?

El uso del programa es realmente sencillo, si queremos resaltar el código fuente, lo primero que demos de hacer es cargar el estilo para el lenguaje que queremos, para eso hacemos clic en el boton "Cargar estilo", una vez cargado, debemos de abrir el archivo de código fuente o pegar el código fuente directamente en el editor, si elegimos la opción de "Abrir", en cuanto cargue el archivo, intentará colorearlo dependiendo del estilo cargado, por lo que si no hay ninguno previamente cargado, recorrerá todo el archivo y no pintará nada.

Para cambiar los colores de cada estilo, debemos de seleccionar que tipo de elemento es al que queremos cambiar el color.
En la lista debemos de seleccionar cual es el tipo de elemento que queremos editar y para editarlo, debemos de hacer clic en el botón "Color" y se abrirá el cuadro de selección de color, cuando hayamos hecho todos los cambios que queremos, debemos de hacer clic en el botón "Volver a colorear", para que los cambios se hagan visibles.

Para editar o crear un nuevo estilo, debemos de hacer clic en el botón "Editar estilo", después se nos abrirá una nueva ventana donde podremos editar las expresiones regulares.
Una vez que hayamos editado la expresión regular para cada apartado, debemos de hacer clic en "Guardar en archivo" para que los cambios se guarden. Para aplicar el estilo, es necesario cargarlo en la ventana principal usando el botón "Cargar estilo".

Para guardar el resultados del resaltado, en la ventana principal, debemos de hacer clic en "Guardar", con esto se creara un archivo de formato enriquecido que podremos abrir en Microsoft Word (o cualquier editor de texto que soporte rtf). Otro cosa que es importante remarcar, es que por ahora, el fondo del texto es transparente, por lo que al exportarlo y abrirlo con Word, veremos que todas las letras que estén en blanco no se verán, para eso es necesario cambiar el color de la hoja.

Así se ve con el fondo cambiado a negro.

Así se ve con el fondo por default

Los leo luego

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.

Vamos a programar #67 - Syntax Highlighter M (beta)

Hola de nuevo a todos, el día de hoy vamos a continuar con más sobre expresiones regulares, pero mas específicamente, cómo resaltar la sintaxis usando código en C#.

En el post anterior vimos una prueba de concepto (Proof Of Concept {POC}) de un programa que sirve para realzar la sintaxis, pero debido a varias razones que son difíciles de explicar (o porque soy flojo), no todo el código estaba implementado. Por eso hoy veremos cómo es que funciona la versión beta de este programa y explicaré un poco que hace cada función.

Antes que nada, veamos el código que hace funcionar las cosas:

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.IO;
using System.Text.RegularExpressions;
using System.Threading;

namespace SyntaxHighlighter
{
	public partial class FrmMain : Form
	{
		private enum WordTypes : int
		{
			WType1 = 0,
			WType2,
			WType3,
			NType,
			OpType,
			SType,
			CType,
			OtherType
		}
		#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"];
		#endregion

	# region "Palabras reservadas"
		private string ReservedWords1 = @"\b(as|base|bool|break|byte|case|catch|char|checked|class|continue|decimal|default|delegate|do|double|else|enum|explicit|false|finally|fixed|float|for|foreach|goto|if|implicit|in|int|interface|is|lock|long|namespace|null|object|operator|out|params|ref|return|sbyte|short|sizeof|stackalloc|string|struct|switch|this|throw|true|try|typeof|uint|ulong|unchecked|ushort|using|var|void|while)\b";
		private string ReservedWords2 = @"\b(public|private|internal|protected|abstract|async|const|event|extern|new|override|partial|readonly|sealed|static|unsafe|virtual|volatile)\b";
		private string Comments = @"((\/\/.+)|(\/\*[\s\S]*?\*\/)|\b(\\\\)\b)";
		private string Numbers = @"((0[xX])(\d|[a-f|A-F])+)|(\b(\d+)\b)";
		private string Strings = "(\".*?\")";
		private string Operators = @"\[|\]|\(|\)|\.|\?|\b(and)\b|(\:\:)|\+|\-|\*|\/|\%|\&|\^|\!|\~|\=|\<|\>|\?\:|\++\|\-\-|\&\&|\<\<|\>\>|\=\=|\!\=|\<\=|\>\=|\+\=|\-\=|\*\=|\/\=|\%\=|\&\=|\=|\^\=|\<\<\=|\>\>\=|\-\>|\?\?|\=\>|";
	#endregion

	#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();
		}

		public FrmMain()
		{
			InitializeComponent();
		}

		private void ColorizeAll()
		{
			Colorize(ReservedWords1, WordTypes.WType1);
			Colorize(ReservedWords2, WordTypes.WType2);
			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;
			}
			//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);
			}
		}

		private void TSBtnOpen_Click(object sender, EventArgs e)
		{
			OpenFileDialog OpDiag = new OpenFileDialog();
			OpDiag.Filter = "Código fuente C#|*.cs|Arvhivos de texto|*.txt|Todos los archivos|*.*";
			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)
		{
			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)
		{
			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)
		{
			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;
		}

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

		private void TSLBWordTypes_TextChanged(object sender, EventArgs e)
		{

		}
	}
}

La explicación de cada parte en orden de aparición.

Primero tenemos una enumeración llamada "WordTypes" que hereda del tipo "int", esto solo es para poder usar su valor de manera mas sencilla. La enumeración esta compuesta  por los elementos:

  • "WType1" - y su valor es 0
  • "WType2" - Su valor es 1
  • "Wtype3" - Su valor es 2
  • "NType" - Su valor es 3
  • "OpType" - Su valor es 4
  • "SType" - Su valor es 5
  • "CType" - Su valor es 6
  • "OtherType" - Su valor es 7
Solo para tenerlo en cuenta, "WType" es por Word Type, "NType" es por Numbers Type, "OpType" es por Operator Type, "SType" es por String Type, "CType" es por Comment Type y "OtherType" su nombre es muy explicatorio. Si bien recuerdo, en la gran mayoría de los lenguajes se cubren todos los tipos de palabras reservadas, si alguno requiriera de algún tipo más, se harán las adecuaciones para que se puedan escoger mas tipos, pero por ahora cubrimos gran parte y el principal por ahora que es C#.

Después creamos la función "UpdateRTB()", está función servirá para poder darle los estilos al texto en el control "RTBMain" y hay dos razones para usar un procedimiento para esa tarea. El primero es que esta acción se va a realizar mas de una vez y la otra; es para poder usarla desde un "Thread" (alguna vez dije que en lugar de "Thread" usaría hilo pero naahh) y por eso es que justo antes de crear el procedimiento, también creamos un delegado que contiene los mismos parámetros que la función. La función recibe tres parámetro, el primero del tipo "int", el segundo del tipo "int" y el tercero del tipo "Color", dentro de la función lo primero que revisa es si el control esta siendo invocado, si es así, manda a llamar al delegado para que este establezca los parámetros. Si hacemos un poco de memoria, en el post anterior, vimos que para resaltar una palabra usábamos tres valores (de ahí los tres parámetros), el primero era donde iba a comenzar la selección, el segundo que tanto se iba a seleccionar y el tercero era el color para el resaltado. Con las adecuaciones que hicimos, ahora podemos hacer el resaltado desde otro "Thread".

La siguiente función es "GetText()", está función tiene cómo objetivo obtener el texto del control "RTBMain" Al igual que la función anterior, primero prueba si el control está siendo invocado, si es asi la funcion llama al delegado "GetTextDelegate" y regresa un valor del tipo "string" con el texto que el delegado obtuvo, en caso contrario devuelve un valor del tipo "string" con el texto de "RTBMain"
Aquí vemos la importancia de los delgados. Ya los veremos mas adelante ;)
La siguiente función es "DoTheWork()", está función crea un "Thread" diferente y ejecuta la función "ColorizeAll()"

El siguiente procedimiento es "ColorizeAll()", esta función su trabajo es llamar a la función "Colorize", llamándola tantas veces cómo tipos de palabras reservadas tenemos, debido a cómo está hecho el programa, aquí es muy importante conservar el orden (incluso ahora dos de los elementos están al revés por lo que podrás notar que alguna cosas tienen color que no deben, eso si, nada grave).

La siguiente función es "SaveColor". Esta función recibe dos parámetros, el primero es del tipo "WorTypes" y el segundo es del tipo "Color", esta función se usa para guardar los colores que el usuario elija para cada tipo de palabra reservada, usando la instrucción "switch", comprueba de que caso se trata y dependiendo de que valor sea "Tipo"; que es del tipo "WordTypes";  y guarda el valor en la configuración del programa.

Finalmente el procedimiento "Colorize". Recibe dos parámetro, el primero del tipo "string" que es el patrón de expresión regular que se va a buscar y el segundo del tipo "WordTypes". Al igual que el procedimiento para guardar las configuraciones, en este procedimiento "probamos" cual de los posibles valores es el que se va a evaluar y a partir de ahí cambiar al color que se designó para el resaltado.

Al implementar todas la funciones, el programa ya sirve, pero aun es beta, puesto que tienen errores básicos. Todos esto los arreglaremos, pero primero trataremos de implementar las funciones que serian "de cajón", es decir resaltar la sintaxis de otros lenguajes y no solo de C#. Eso lo haremos en el siguiente post, continuaremos con mas de este programa.

Y bien, por ahora es todo, cómo de costumbre, puedes bajar el código de mi dropbox para probarlo y modificarlo.

Los leo luego.