Colabora .NET

Complemento para ayuda en la escritura de código

Cómo crear un complemento que nos permita crear nuestras propias palabras clave que serán reemplazadas por su correspondiente fragmento de código en VS.2003

 

Fecha: 31/Ago/2006 (30 Agosto 2006)
Autor: Santiago Barro - santiagobarro@gmail.com

 


Introducción

Siempre me ha fascinado el apoyo que nos ofrece el editor de Visual Studio a la hora de escribir nuestro código. Escribiendo "Try", "Sub Procedimiento" o "Select Case x" el editor de Visual Studio se encarga de añadir el fragmento de código correspondiente acelerando nuestra productividad y evitando posibles errores.

En este, mi primer artículo, intentaré explicar cómo podemos ampliar esta funcionalidad mediante un complemento con VS.2003 que nos permita añadir nuestras propias palabras clave que sean reemplazadas por su correspondiente fragmento de código.

 

Creando el nuevo complemento

Creamos un nuevo proyecto del tipo "Complemento de Visual Studio.NET" y lo denominamos "AddinCodigo". Este tipo de proyecto se encuentra en "Otros proyectos \ Proyectos de extensibilidad".

Al aceptar el nombre del proyecto, nos saldrá un asistente para complementos. En las correspondientes pestañas iremos introduciendo los siguientes parámetros:

  • Seleccione un lenguaje de programación: Selecciona "Crear un complemento utilizando Visual Basic"

  • Seleccione una aplicación host: Dejar como está.

  • Nombre del complemento: AddinCodigo

  • Descripción del complemento: Complemento para la ayuda en la escritura de código.

  • Elija las opciones del complemento: Seleccionar la opción "Me gustaría que se cargara al iniciar la aplicación host".

  • Selección de información para Ayuda - Acerca de: Por último, si lo deseas, puedes agregar información a la ventana Acerca de.

Comprobamos que, al finalizar el asistente, nos han sido creados 2 proyectos: "AddinCodigo" y "AddinCodigoSetup".

 

Escribiendo el código

Modificamos la clase "Connect.vb" insertando el siguiente código:

   Public Class Connect
        Implements Extensibility.IDTExtensibility2

#Region " Variables "
    Dim StarthPath As String
    Dim applicationObject As EnvDTE.DTE
    Dim TextEditorEvents As TextEditorEvents
    Dim dsetCfg As DataSet
#End Region

    Public Sub OnBeginShutdown(ByRef custom As System.Array) _
        Implements Extensibility.IDTExtensibility2.OnBeginShutdown
    End Sub


    Public Sub OnAddInsUpdate(ByRef custom As System.Array) _
        Implements Extensibility.IDTExtensibility2.OnAddInsUpdate
    End Sub

    Public Sub OnStartupComplete(ByRef custom As System.Array) _
        Implements Extensibility.IDTExtensibility2.OnStartupComplete
    End Sub

    Public Sub OnDisconnection(ByVal RemoveMode As Extensibility.ext_DisconnectMode, _
                               ByRef custom As System.Array) _
        Implements Extensibility.IDTExtensibility2.OnDisconnection
        TextEditorEvents = Nothing
        dsetCfg = Nothing
    End Sub

   Public Sub OnConnection(ByVal application As Object, _
                           ByVal connectMode As Extensibility.ext_ConnectMode, _
        ByVal addInInst As Object, ByRef custom As System.Array) _
        Implements Extensibility.IDTExtensibility2.OnConnection

        applicationObject = DirectCast(application, EnvDTE.DTE)
        TextEditorEvents = applicationObject.Events.TextEditorEvents(Nothing)
        AddHandler TextEditorEvents.LineChanged, AddressOf TextEditorEvents_LineChanged

        'Obtenemos el directorio del complemento
        StarthPath = System.Reflection.Assembly.GetExecutingAssembly().Location
        Dim UltBarra As Integer = StarthPath.LastIndexOf("\")
        StarthPath = StarthPath.Remove(UltBarra, StarthPath.Length - UltBarra)

        'Cargamos el archivo de configuración
        CargaCfg()

    End Sub


#Region " Carga cfg "
    Private Sub CargaCfg()
        'Cargamos el archivo de configuración
        Try
            Dim Archivo As String = StarthPath & "\cfg.xml"
            If IO.File.Exists(Archivo) = False Then Exit Sub
            dsetCfg = New DataSet
            dsetCfg.ReadXml(Archivo)
        Catch ex As Exception
            MsgBox("Ha ocurrido un error al intentar cargar el archivo de configuración." & _
                   vbNewLine & _
                   "Detalle: " & ex.Message, MsgBoxStyle.Critical, "")
        End Try
    End Sub
#End Region

#Region " Analiza Texto "
    Protected Sub TextEditorEvents_LineChanged(ByVal StartPoint As EnvDTE.TextPoint, _
        ByVal EndPoint As EnvDTE.TextPoint, ByVal Hint As Integer)
        Try
            Dim Linea As String = StartPoint.CreateEditPoint().GetText(EndPoint)
            AnalizaTexto(Linea)
        Catch ex As Exception
            MsgBox(ex.Message, MsgBoxStyle.Critical, "AddinCodigo")
        End Try
    End Sub

    Private Sub AnalizaTexto(ByVal Texto As String)
        Try
            'Analiza el texto introducido
            If IsNothing(Texto) Or Texto = "" Then Exit Sub
            Texto = Texto.Trim.ToLower
            If dsetCfg Is Nothing Then Exit Sub

            'Si el texto tiene alguna comilla, no analizamos el texto
            If Texto.IndexOf("'") >= 0 Then Exit Sub

            'Comprobamos si existe como palabra clave en el archivo de configuración
            Dim filas() As DataRow = dsetCfg.Tables("Claves").Select("Clave='" & Texto & "'")
            If filas.Length > 0 Then
                'Clave encontrada
                RealizaAccion(filas)
            End If
        Catch ex As Exception
            MsgBox("Ha ocurrido un error al intentar analizar el texto." & vbNewLine & _
            "Detalle: " & ex.Message, MsgBoxStyle.Critical, "AddinCodigo")
        End Try
    End Sub

    Private Sub RealizaAccion(ByVal filas() As DataRow)
        'Realiza la acción según las instrucciones de las filas
        Dim objTextSelection As TextSelection
        objTextSelection = CType(applicationObject.ActiveDocument.Selection(), _
                                 EnvDTE.TextSelection)
        For Each fila As DataRow In filas
            Select Case fila("Accion")
                Case "LineUp"       'Mueve el cursor una linea hacia arriba
                    objTextSelection.LineUp()
                Case "LineDown"     'Mueve el cursor una linea hacia abajo
                    objTextSelection.LineDown()
                Case "SelectLine"   'Selecciona la linea
                    objTextSelection.SelectLine()
                Case "NewLine"      'Inserta una linea en blanco
                    objTextSelection.NewLine()
                Case "Insert"       'Escribe el texto indicado
                    objTextSelection.Insert(fila("texto"))
                Case "Collapse"     'Contraer a definiciones
                    applicationObject.ExecuteCommand("Edit.CollapsetoDefinitions")
            End Select
        Next
    End Sub
#End Region

End Class

Descripción del código

Al iniciarse el complemento, se carga el archivo "cfg.xml", que deberá encontrarse en la carpeta de la aplicación, en el dataset "dsetCfg" en el cual se habrá introducido anteriormente las palabras claves a tratar y sus correspondientes acciones.

Capturamos cualquier cambio realizado en la escritura de código mediante el evento "TextEditorEvents_LineChanged" a través del cual comprobamos si se ha introducido una palabra clave. Si es así, ejecuta el procedimiento "RealizaAccion" que es el encargado de ejecutar las acciones definidas en el dataset "dsetCfg".

 

Estructura del archivo XML

Está compuesto por una sola tabla "Claves", la cual se compone de 3 columnas: "Clave", "Accion" y "Texto".

  • Clave: Indica el nombre de la palabra clave

  • Accion: Acción a realizar. Los posibles parámetros son:

    • LineUp: Mueve el cursor una línea hacia arriba.

    • LineDown: Mueve el cursor una línea hacia abajo.

    • SelectLine: Selecciona la línea activa.

    • NewLine: Inserta una línea en blanco.

    • Insert: Escribe el texto indicado en el campo "Texto".

    • Collapse: Contrae todas las definiciones.

  • Texto: Texto a escribir cuando la acción sea "Insert".

Ejemplo:

Si queremos crear una palabra clave "ForFila" que inserte el texto "For Each fila As DataRow in Dset.Tables(0).Rows" añadiríamos las siguientes filas al archivo XML:

 

<Claves>
	<Clave>ForFila</Clave>
	<Accion>LineUp</Accion>
	<Texto></Texto>
</Claves>


<Claves>
	<Clave>ForFila</Clave>
	<Accion>SelectLine</Accion>
	<Texto></Texto>
</Claves>


<Claves>
	<Clave>ForFila</Clave>
	<Accion>Insert</Accion>
	<Texto>For Each fila As DataRow in Dset.Tables(0).Rows</Texto>
</Claves>


<Claves>
	<Clave>ForFila</Clave>
	<Accion>LineDown</Accion>
	<Texto></Texto>
</Claves>

 

Instalando el complemento

Procederemos a la generación del proyecto de instalación del complemento pulsando con el botón derecho sobre el proyecto "AddinCodigoSetup" y eligiendo la opción "Generar".

Una vez terminada la generación del proyecto de instalación volveremos a pulsar con el botón derecho sobre el mismo proyecto y elegiremos esta vez la opción "Instalar".

 

Recordar que deberá incluirse el archivo "cfg.xml" en el proyecto de instalación o añadirlo manualmente después en la carpeta de la aplicación. En el proyecto de ejemplo ya ha sido incluido en el proyecto de instalación.

 

 

Por último sólo nos queda activar el complemento para que se ejecute automáticamente al abrir Visual Studio. Ejecutamos la opción "Herramientas \ Administrador de complementos" y seleccionamos el complemento "AddinCodigo" y activamos la opción "Inicio".

 

 

En el fichero de ejemplo, adjunto un archivo "cfg.xml" con algunas palabras ya configuradas. Escribe Ayuda en el editor de código y se mostrará la descripción de cada una de ellas.

En un futuro y, si tengo tiempo :-), intentaré diseñar una pantalla para poder configurar las palabras clave de una manera más cómoda.

 

Espero que este complemento les sea de mucha utilidad.

 

 


Espacios de nombres usados en el código de este artículo:

Extensibility
System.Runtime.InteropServices
EnvDTE

 


Código de ejemplo (ZIP):

 

Fichero con el código de ejemplo: SantiagoBarro_AddinCodigo.zip - (34 KB)

(MD5 checksum: 09A2D5C8D81D52E5BC4B5756B872E560)

 


ir al índice principal del Guille