在舊版語言服務中的自訂文件屬性
發行︰ 2016年7月
如需 Visual Studio 2017 的最新文件請參閱 Visual Studio 2017 文件。
文件內容可以顯示在Visual Studio屬性視窗。 程式設計語言通常不需要個別的原始程式檔相關聯的屬性。 不過,XML 支援會影響編碼方式、 結構描述和樣式表的文件屬性。
討論
如果您的語言需要自訂文件屬性,您必須衍生自DocumentProperties類別,並在您的衍生類別上實作所需的內容。
此外,文件屬性通常會儲存在原始程式檔本身。 需要剖析原始程式檔中顯示的屬性資訊的語言服務屬性視窗,並更新原始程式檔,當變更中的文件屬性屬性視窗。
自訂 DocumentProperties 類別
若要支援自訂文件屬性,您必須衍生自DocumentProperties類別並新增您需要任意數目的屬性。 您也應該提供組織中的使用者屬性屬性] 視窗中顯示。 如果屬性只有get
存取子,以唯讀模式中顯示屬性視窗。 如果屬性同時get
和set
存取子中,屬性也可以在更新屬性視窗。
範例
以下是範例類別衍生自DocumentProperties,顯示兩個屬性的檔案名稱和描述。 更新屬性時,自訂方法上的LanguageService類別稱為 「 原始程式檔中寫入屬性。
using System.ComponentModel;
using Microsoft.VisualStudio.Package;
namespace TestLanguagePackage
{
class TestDocumentProperties : DocumentProperties
{
private string m_filename;
private string m_description;
public TestDocumentProperties(CodeWindowManager mgr)
: base(mgr)
{
}
// Helper function to initialize this property without
// going through the FileName property (which does a lot
// more than we need when the filename is set).
public void SetFileName(string filename)
{
m_filename = filename;
}
// Helper function to initialize this property without
// going through the Description property (which does a lot
// more than we need when the description is set).
public void SetDescription(string description)
{
m_description = description;
}
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
// The document properties
[CategoryAttribute("General")]
[DescriptionAttribute("Name of the file")]
[DisplayNameAttribute("Filename")]
public string FileName
{
get { return m_filename; }
set
{
if (value != m_filename)
{
m_filename = value;
SetPropertyValue("Filename", m_filename);
}
}
}
[CategoryAttribute("General")]
[DescriptionAttribute("Description of the file")]
[DisplayNameAttribute("Description")]
public string Description
{
get { return m_description; }
set
{
if (value != m_description)
{
m_description = value;
SetPropertyValue("Description", m_description);
}
}
}
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
///
/////
// Private methods.
private void SetPropertyValue(string propertyName, string propertyValue)
{
Source src = this.GetSource();
if (src != null)
{
TestLanguageService service = src.LanguageService as TestLanguageService;
if (service != null)
{
// Set the property in to the source file.
service.SetPropertyValue(src, propertyName, propertyValue);
}
}
}
}
}
具現化自訂 DocumentProperties 類別
若要具現化您的自訂文件內容類別,您必須覆寫CreateDocumentProperties方法的版本中LanguageService類別,以傳回的單一執行個體您DocumentProperties類別。
範例
using System.ComponentModel;
using Microsoft.VisualStudio.Package;
namespace TestLanguagePackage
{
class TestLanguageService : LanguageService
{
private TestDocumentProperties m_documentProperties;
public override DocumentProperties CreateDocumentProperties(CodeWindowManager mgr)
{
if (m_documentProperties == null)
{
m_documentProperties = new TestDocumentProperties(mgr);
}
return m_documentProperties;
}
}
}
原始程式檔中的屬性
因為文件屬性通常是原始程式檔,值會儲存在原始程式檔本身。 這需要支援的語言剖析器或掃描器來定義這些屬性。 例如,XML 文件的屬性會儲存在根節點上。 根節點上的值會修改時屬性視窗值會變更,並在編輯器中更新的根節點。
範例
這個範例會將儲存 「 檔案名稱 」 和 「 說明 」 中的前兩行的原始程式檔,做為內嵌在特殊的註解標頭中的屬性︰
//!Filename = file.testext
//!Description = A sample file
這個範例示範取得和設定從原始程式檔的前兩行的文件內容如何屬性會更新,如果使用者直接修改原始程式檔所需的兩個方法。 SetPropertyValue
顯示下面是相同的範例中的方法呼叫其中一個從TestDocumentProperties
類別,如 「 自訂 DocumentProperties 類別 」 一節所示。
此範例使用掃描器來判斷前兩行中的權杖類型。 這個範例是僅供示範用途。 這種情況下更常見的作法是剖析原始程式檔到所謂剖析樹狀結構其中樹狀結構的每個節點都包含特定語彙基元的相關資訊。 根節點會包含文件屬性。
using System.ComponentModel;
using Microsoft.VisualStudio.Package;
namespace TestLanguagePackage
{
// TokenType.Comment is the last item in that enumeration.
public enum TestTokenTypes
{
DocPropertyLine = TokenType.Comment + 1,
DocPropertyName,
DocPropertyAssign,
DocPropertyValue
}
class TestLanguageService : LanguageService
{
// Search this many lines from the beginning for properties.
private static int maxLinesToSearch = 2;
private TestDocumentProperties m_documentProperties;
// Called whenever a full parsing operation has completed.
public override void OnParseComplete(ParseRequest req)
{
if (m_documentProperties != null)
{
Source src = GetSource(req.View);
if (src != null)
{
string value = GetPropertyValue(src, "Filename");
m_documentProperties.SetFileName(value);
value = GetPropertyValue(src, "Description");
m_documentProperties.SetDescription(value);
// Update the Properties Window.
m_documentProperties.Refresh();
}
}
}
// Retrieves the specified property value from the given source.
public string GetPropertyValue(Source src, string propertyName)
{
string propertyValue = "";
if (src != null)
{
IVsTextColorState colorState = src.ColorState;
if (colorState != null)
{
string line;
TokenInfo[] lineInfo = null;
int i;
for (i = 0; i < maxLinesToSearch; i++)
{
line = src.GetLine(i);
lineInfo = src.GetColorizer().GetLineInfo(
src.GetTextLines(),
i,
colorState);
if (lineInfo == null)
{
continue;
}
if (lineInfo[0].Type != (TokenType)TestTokenTypes.DocPropertyLine)
{
// Not a property line.
continue;
}
TokenInfo valueInfo = new TokenInfo();
int tokenIndex = -1;
for (tokenIndex = 0;
tokenIndex < lineInfo.Length;
tokenIndex++)
{
if (lineInfo[tokenIndex].Type == (TokenType)TestTokenTypes.DocPropertyName)
{
break;
}
}
if (tokenIndex >= lineInfo.Length)
{
// No property name on the line.
continue;
}
string name = src.GetText(i,
lineInfo[tokenIndex].StartIndex,
i,
lineInfo[tokenIndex].EndIndex + 1);
if (name != null)
{
if (String.Compare(name, propertyName, true) == 0)
{
for ( ;
tokenIndex < lineInfo.Length;
tokenIndex++)
{
if (lineInfo[tokenIndex].Type == (TokenType)TestTokenTypes.DocPropertyValue)
{
break;
}
}
if (tokenIndex < lineInfo.Length)
{
propertyValue = src.GetText(i,
lineInfo[tokenIndex].StartIndex,
i,
lineInfo[tokenIndex].EndIndex + 1);
}
break;
}
}
}
}
}
return propertyValue;
}
// Sets the specified property into the given source file.
// Called from the TestDocumentProperties class.
public void SetPropertyValue(Source src, string propertyName, string propertyValue)
{
string newLine;
if (propertyName == null || propertyName == "")
{
// No property name, so nothing to do
return;
}
if (propertyValue == null)
{
propertyValue = "";
}
// This is the line to be inserted.
newLine = String.Format("//!{0} = {1}", propertyName, propertyValue);
// First, find the line on which the property belongs.
// If line is found, replace it.
// Otherwise, insert the line at the beginning of the file.
if (src != null)
{
IVsTextColorState colorState = src.ColorState;
if (colorState != null)
{
int lineNumber = -1;
string line;
TokenInfo[] lineInfo = null;
int i;
for (i = 0; i < maxLinesToSearch; i++)
{
line = src.GetLine(i);
lineInfo = src.GetColorizer().GetLineInfo(
src.GetTextLines(),
i,
colorState);
if (lineInfo == null)
{
continue;
}
if (lineInfo[0].Type != (TokenType)TestTokenTypes.DocPropertyLine)
{
// Not a property line
continue;
}
TokenInfo valueInfo = new TokenInfo();
int tokenIndex = -1;
for (tokenIndex = 0;
tokenIndex < lineInfo.Length;
tokenIndex++)
{
if (lineInfo[tokenIndex].Type == (TokenType)TestTokenTypes.DocPropertyName)
{
break;
}
}
if (tokenIndex >= lineInfo.Length)
{
// No property name on the line.
continue;
}
string name = src.GetText(i,
lineInfo[tokenIndex].StartIndex,
i,
lineInfo[tokenIndex].EndIndex + 1);
if (name != null)
{
if (String.Compare(name, propertyName, true) == 0)
{
lineNumber = i;
break;
}
}
}
// Set up an undo context that also handles the insert/replace.
EditArray editArray = new EditArray(src,
true,
"Update Property");
if (editArray != null)
{
TextSpan span = new TextSpan();
if (lineNumber != -1)
{
// Replace line.
int lineLength = 0;
src.GetTextLines().GetLengthOfLine(lineNumber,
out lineLength);
span.iStartLine = lineNumber;
span.iStartIndex = 0;
span.iEndLine = lineNumber;
span.iEndIndex = lineLength;
}
else
{
// Insert new line.
span.iStartLine = 0;
span.iStartIndex = 0;
span.iEndLine = 0;
span.iEndIndex = 0;
newLine += "\n";
}
editArray.Add(new EditSpan(span, newLine));
editArray.ApplyEdits();
}
}
}
}
}
}