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

No hay comentarios.