Make Notepad++ auto close HTML/XML tags after the slash(the Dreamweaver way)

时间:2023-03-10 01:43:01
Make Notepad++ auto close HTML/XML tags after the slash(the Dreamweaver way)

I've been looking for a Notepad++ plugin that can close HTML/XML tags after a slash just like the way Dreamweaver does for a long time.
The only things I could find(TextFX, XML Tools etc.) close the tags right after ">" is typed in, which was not what I wanted.
A couple days ago I found a plugin called Automation Scripts. It allows me to write scripts in C#, so I don't have to spend time learning how to write Notepad++ plugins.
So here you go:

//npp_shortcut Ctrl+Shift+Z

//Automation Scripts plugin needs to be installed for this to work
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Text;
using System;
using System.Windows.Forms;
using NppScripts; public class Script : NppScript
{
public Script()
{
this.OnNotification = (notification) =>
{
if(notification.nmhdr.code == (uint)SciMsg.SCN_CHARADDED)
{
doInsertHtmlCloseTag((char)notification.ch);
}
};
} public override void Run()
{
checkInsertHtmlCloseTag();
} bool doCloseTag; void checkInsertHtmlCloseTag()
{
doCloseTag = !doCloseTag;
Win32.SendMessage(Npp.NppHandle, NppMsg.NPPM_SETMENUITEMCHECK, Plugin.FuncItems.Items[this.ScriptId]._cmdID, doCloseTag ? : );
} string GetStartTagName(string input)
{
Regex regex=new Regex(@"<[A-Za-z][A-Za-z0-9]*[^>]*[^\/]>|<[A-Za-z]>");
if(!regex.IsMatch(input)) return "";
StringBuilder result=new StringBuilder();
int i=;
while(input[i]!=' ' && input[i]!='>' & i<input.Length)
{
result.Append(input[i]);
i++;
}
return result.ToString();
} string GetEndTagName(string input)
{
Regex regex=new Regex(@"<\/[A-Za-z][A-Za-z0-9]*[^>]*>");
if(!regex.IsMatch(input)) return "";
StringBuilder result=new StringBuilder();
int i=;
while(input[i]!=' ' && input[i]!='>' & i<input.Length)
{
result.Append(input[i]);
i++;
}
return result.ToString();
} string GetSelection()
{
IntPtr hCurrentEditView = Npp.CurrentScintilla;
StringBuilder result=new StringBuilder();
Win32.SendMessage(hCurrentEditView,SciMsg.SCI_GETSELTEXT,,result);
return result.ToString();
} int FindNextTag(int pos)
{
IntPtr hCurrentEditView = Npp.CurrentScintilla;
string pattern=@"<\/?[A-Za-z][A-Za-z0-9]*[^>]*[^\/]>|<\/?[A-Za-z]>";
Win32.SendMessage(hCurrentEditView, SciMsg.SCI_SETSEL, pos, pos);
Win32.SendMessage(hCurrentEditView,SciMsg.SCI_SEARCHANCHOR,,);
return (int)Win32.SendMessage(hCurrentEditView,SciMsg.SCI_SEARCHPREV,(int)SciMsg.SCFIND_REGEXP,pattern);
} void doInsertHtmlCloseTag(char newChar)
{
LangType docType = LangType.L_TEXT;
Win32.SendMessage(Npp.NppHandle, NppMsg.NPPM_GETCURRENTLANGTYPE, , ref docType);
bool isDocTypeHTML = (docType == LangType.L_HTML || docType == LangType.L_XML || docType == LangType.L_PHP);
if (doCloseTag && isDocTypeHTML && newChar=='/')
{
IntPtr hCurrentEditView = Npp.CurrentScintilla;
int currentPos = (int)Win32.SendMessage(hCurrentEditView, SciMsg.SCI_GETCURRENTPOS, , );
char lastChar=(char)Win32.SendMessage(hCurrentEditView,SciMsg.SCI_GETCHARAT,currentPos-,);
StringBuilder insertString=new StringBuilder();
if(lastChar=='<')
{
int pos=currentPos;
Stack<string> stack=new Stack<string>();
string tag;
while(true)
{
pos=FindNextTag(pos);
if(pos==-)
{
Win32.SendMessage(hCurrentEditView, SciMsg.SCI_SETSEL, currentPos, currentPos);
return;
}
tag=GetSelection();
if(tag[]=='/')
{
stack.Push(GetEndTagName(tag));
}
else
{
tag=GetStartTagName(tag);
if(stack.Count==)
break;
else
{
string endTag=stack.Pop();
while(tag!=endTag && stack.Count>)
{
endTag=stack.Pop();
}
if(tag!=endTag) break;
}
}
}
insertString.Append(tag+">");
Win32.SendMessage(hCurrentEditView, SciMsg.SCI_BEGINUNDOACTION, , );
Win32.SendMessage(hCurrentEditView, SciMsg.SCI_SETSEL, currentPos, currentPos);
Win32.SendMessage(hCurrentEditView, SciMsg.SCI_REPLACESEL, , insertString);
Win32.SendMessage(hCurrentEditView, SciMsg.SCI_SETSEL, currentPos+insertString.Length, currentPos+insertString.Length);
Win32.SendMessage(hCurrentEditView, SciMsg.SCI_ENDUNDOACTION, , );
}
}
}
}