Trình soạn thảo dựa trên CDT, Phần 2 : Trình bày văn bản trong CDT

Làm nổi bật mã nguồn bằng kiểu dáng cú pháp

Bài này là bài thứ hai trong loạt bài 5 phần "Trình soạn thảo dựa trên CDT" giới thiệu cách trình bày văn bản trong công cụ phát triển C/C++ của Eclipse (CDT). Việc trình bày văn bản là ưu điểm quan trọng của CDT. Việc hiển thị rõ ràng, nhiều màu sắc làm cho nó trở nên dễ đọc và dễ điều hướng thông qua mã. Sự hiểu biết cách hoạt động là rất quyết định, cho dù bạn muốn hiểu mã CDT hoặc xây dựng một trình soạn thảo nguồn đầy đủ tính năng cho riêng bạn. Hơn nữa, các cơ chế cho phép trình bày văn bản của CDT được chấp nhận cũng cần thiết đối với khả năng quan trọng hơn, đó là: phân tích cú pháp tự động.

Matthew Scarpino, Phát triển Java, Eclipse Engineering, LLC

Matthew Scarpino là nhà quản lý dự án và nhà phát triển Java tại Eclipse Engineering LLC. Ông là tác giả hàng đầu của SWT/JFace in Action và có đóng góp nhỏ nhưng quan trọng vào Standard Widget Toolkit (SWT). Ông thích nhạc dân gian Ailen, chạy maratông, thơ của William Blake, và Graphical Editing Framework (GEF).



10 09 2010

Giới thiệu về trình bày văn bản của CDT

Với mỗi ký tự mà bạn nhập, trình soạn thảo của CDT sẽ thực hiện một lượng lớn các tác vụ. Nó kiểm tra các thay đổi tại các phân vùng của tài liệu và kích hoạt các quy tắc để tiếp tục phân chia văn bản. Nếu ký tự này dùng cho một chức năng, thì trình soạn thảo cho phép thường trình con gấp lại để giảm thiểu không gian văn bản. Nếu ký tự này thuộc vào là một từ, thì trình soạn thảo xác định xem từ đó có nên được thêm vào chỉ mục không. Hơn nữa, nó xác định xem ký tự đó phù hợp với cấu trúc được chấp nhận của một tài liệu C/C++ hay không. Nếu phù hợp, nó sẽ cập nhật Mô hình đối tượng tài liệu (Document Object Model-DOM) nội tại của nó. Nếu không, nó sẽ cung cấp báo cáo lỗi thông qua các chú giải.

Bao quát mọi khía cạnh xử lý sự kiện CDT là nằm ngoài phạm vi của bài này, do đó chúng tôi sẽ tập trung vào kiểu dáng cú pháp. Chúng tôi sẽ giải thích cách trình soạn thảo thay đổi màu sắc của văn bản và kiểu phông chữ dựa trên cấu trúc của mã nguồn. Không những chỉ cho thấy cách trình soạn thảo tương tác với gõ phím, mà các đối tượng và các quy trình cũng sẽ được sử dụng trong Phần 3 của loạt bài này, trong đó thảo luận về phân tích cú pháp của CDT.

Tôi đã cập nhật công cụ phát triển C/C++ lược giản (BBCDT) ở Phần 1 để cung cấp cùng một hiển thị văn bản ở bài này. Các lớp mới nằm trong các gói org.bbcdt.dworks.internal.ui.textorg.bbcdt.dworks.core.parser. Nếu bạn nhập mã hợp lệ vào một tệp nguồn của BBCDT, bạn sẽ thấy cùng kiểu dáng cú pháp mà bạn đã quen với bản CDT đầy đủ (xem hình 1). Xem phần Tải về để lấy mã.

Hình 1. Tạo kiểu dáng cú pháp của CDT
Tạo kiểu dáng cú pháp của CDT

Quá trình tạo kiểu dáng cú pháp của CDT

Việctạo kiểu dáng cú pháp có vẻ thông dụng, có tính trang trí, nhưng quá trình này không đơn giản. Như bạn sẽ thấy, sẽ có rất nhiều thứ đang diễn ra. Tin tốt lành là một khi nó trở nên rõ ràng, bạn có thể tùy chỉnh mọi màu sắc và phông chữ theo ý thích của mình và chắc chắn rằng trình soạn thảo của bạn sẽ chính xác theo cách mà bạn muốn. Vì hầu hết các lớp là một phần của giao diện lập trình ứng dụng (API) của trình soạn thảo văn bản của Eclipse, nên bạn có thể sử dụng chúng trực tiếp trong các trình soạn thảo của riêng mình.

Nói một cách đơn giản thì mục đích cuối cùng của việc tạo kiểu dáng cú pháp là tạo ra đối tượng TextPresentation cho một phần văn bản bất cứ khi nào người dùng nhập một chuỗi mã C/C++ hợp lệ. Quá trình này bao gồm bốn bước sau:

  1. Document tạo ra DocumentEvent đối với động tác gõ phím nhập.
  2. Lớp FastPartitioner cập nhật các phân vùng của Document.
  3. Trình khung nhìn cảnh báo lớp PresentationReconciler, lớp này sử dụng lớp DefaultDamagerRepairer để phân tích các phân vùng đã thay đổi.
  4. Lớp DefaultDamagerRepairer sử dụng các quy tắc để tạo ra lớp TextPresentation lớp này cập nhật màu sắc và kiểu dáng của văn bản.

Bước 1. Lớp SourceViewer và lớp Document

Trong quá trình tạo ra, một trong những đối tượng đầu tiên mà CEditor tạo ra là CSourceViewer. Đối tượng này không chỉ kiến thiết hướng dẫn từng bước StyledText của trình soạn thảo mà còn xử lý bất kỳ sự kiện nhận được. Đặc biệt, nó sử dụng lớp VerifyListener để trả lời động tác gõ phím. Bạn có thể chuyển VerifyEvent tới các đối tượng khác khi cần thiết, nhưng bộ khung nhìn cho biết Document của trình soạn thảo theo mặc định.

Như đã đề cập trong Phần 1, Document giữ các thông tin về trình soạn thảo và DocumentProvider sẽ khởi tạo nó bằng văn bản từ tệp đầu vào của trình soạn thảo. Tương tự, SourceViewer cập nhật Document với văn bản của trình soạn thảo. Nó sử dụng DocumentCommand để làm điều này. Mỗi câu lệnh giữ văn bản thêm vào và vị trí của văn bản trong Document. Khi câu lệnh thực thi, nó cập nhật thông tin về mô hình trong Document.

Vì rằng trình soạn thảo CDT chỉ đơn giản là hướng dẫn từng bước StyledText với nhiều thứ trang trí, nên về bản chất thì DocumentString (chuỗi) (về mặt kỹ thuật, là ITextStore). Ngoài văn bản ra, nó chứa một loạt các lớp Position (vị trí) biểu diễn các phần phụ của lớp String. Mỗi lớp Position có chiều dài và khoảng chừa trống, và lớp TypedPosition một cũng có một tên kết hợp. Lớp TypedPosition đặc biệt quan trọng trong thảo luận này vì chúng được sử dụng để biểu diễn các phân vùng.

Khi gói DocumentCommand thực thi, nó bắt đầu bằng cập nhật lớp Position tương ứng với dấu sót (thanh trỏ đứng). Sau đó, câu lệnh gọi phương thức Document.replace(), cho phép thay đổi các ký tự trong lớp ITextStore của gói Document. Khi công việc này kết thúc, gói Document được cập nhật, và nó sẽ gửi các gói DocumentEvent đến bất kỳ gói DocumentListener đã đăng ký.

Bước 2. Phân vùng tài liệu với FastPartitioner

Thay vì phân tích toàn bộ tài liệu mỗi khi bạn nhập văn bản, CDT sử dụng phương pháp tiếp cận chia-và-trị do API của trình soạn thảo văn bản của Eclipse cung cấp. Nghĩa là, nó tách Document thành hai phần loại trừ nhau, gọi là vùng. Bằng cách này thì chỉ có phân vùng chứa văn bản thay đổi sẽ được kiểm tra. Ví dụ, nếu bạn thay đổi một từ trong chú thích nhiều dòng, thì CDT phân tích các phân vùng chứa chú thích và không phân tích phần còn lại của mã.

Tệp plugin.xml trong trình cắm thêm của giao diện người dùng (UI) plug-in sẽ tạo ra phần mở rộng của điểm mở rộng của tệp org.eclipse.core.filebuffers.documentSetup. Khi Document được tạo ra, phần mở rộng này kết nối nó với đối tượng FastPartitioner để xác định và quản lý các đoạn chia nhỏ của nó.

Trong CDT, trình phân vùng này được khởi tạo bởi một mảng gồm bốn chuỗi, mỗi chuỗi đặt tên cho một phân vùng khác nhau:

Mặt sau của việc tách khối

Khi tôi tạo ra một trình soạn thảo đồ họa với API của Khung công tác biên tập đồ họa của Eclipse GEF, hiển nhiên thấy cần tách các vấn đề liên quan. Tôi rất vui là đã có hàng trăm lớp chức năng đơn, chúng kết hợp với trình Mosaic của MVC. Chắc chắn nó phức tạp, nhưng khi xem xét các hình, các liên kết, và các mối tương quan trong trình soạn thảo của GEF, thì điều phức tạp là điều dễ hiểu.

Tuy nhiên, theo tôi thì các trình soạn thảo văn bản không cần mức phức tạp này. Xét cho cùng, nó chỉ là văn bản. Tôi không cần phải đọc đủ các trang của tài liệu để làm mỗi hiển thị tạm thời với chữ màu xanh và đậm. Có thể bạn cho rằng trao đổi của tôi về phân vùng và quy tắc xử lý là quá chi tiết, nhưng thực sự tôi đã loại bớt đi kha khá. Đối với những người không bị ảnh hưởng bởi sự lười biếng và thờ ơ của tôi, thì bạn sẽ làm điều gì đó về việc này ?

  • Ghi chú đa dòng
  • Ghi chú đơn dòng
  • Chuỗi
  • Ký tự

CDT cũng khởi tạo trình phân vùng bằng FastCPartitionScanner. Đơn giản là các trình máy quét của Eclipse chuyển đổi ký tự trong vùng Document thành một loạt dấu hiệu giữ các đối tượng dữ liệu bất kỳ. Trong trường hợp của FastCPartitionScanner, mỗi dấu hiệu chứa một trong bốn chuỗi đặt tên cho phân vùng hiện tại.

Trước khi nó cảnh báo cho bất kỳ trình lắng nghe khác, Document gửi các lớp DocumentEvent đến lớp phân vùng của nó, hoặc trong trường hợp này, chỉ đến lớp FastPartitioner. Lớp phân vùng sử dụng sự kiện để phát hiện thấy dòng đầu tiên có chứa văn bản thay đổi và TypedPosition chứa điểm đầu dòng. Sau đó, nó lệnh cho lớp FastCPartitionScanner chuyển đổi loại văn bản vào đánh dấu.

FastCPartitionScanner đọc các ký tự của Document bằng cách sử dụng lớp BufferedDocumentScanner để xác định liệu một ký tự có biểu diễn điểm kết thúc phân vùng hay không. Nếu đúng như vậy, trình máy quét sẽ trả về đánh dấu Token cho phân vùng đó và báo cáo về vùng trống và chiều dài. FastPartitioner sử dụng hoạt động này để cập nhật danh sách các phân vùng của Document và quá trình phân vùng hoàn tất.

Bước 3. Phân tích văn bản thay đổi bằng DefaultDamagerRepairer

Mặc dù quan trọng, nhưng không có nhiều mã trong lớp CSourceViewer. Gói CSourceViewerConfiguration cung cấp một số đối tượng thực hiện các chức năng cho bộ khung nhìn. Trong số này, một trong những đối tượng quan trọng nhất là PresentationReconciler, nó tạo ra lớp DefaultDamagerRepairer đối với mỗi phân vùng. Các mã trong Liệt kê 1 làm cho điều này có khả năng thực hiện được.

Liệt kê 1. Bổ sung lớp DefaultDamagerRepairers vào gói PresentationReconciler
DefaultDamagerRepairer dr= new \ 
DefaultDamagerRepairer(getSinglelineCommentScanner()); 
reconciler.setDamager(dr, ICPartitions.C_SINGLE_LINE_COMMENT); 
reconciler.setRepairer(dr, ICPartitions.C_SINGLE_LINE_COMMENT); 

dr= new DefaultDamagerRepairer(getMultilineCommentScanner()); 
reconciler.setDamager(dr, ICPartitions.C_MULTILINE_COMMENT); 
reconciler.setRepairer(dr, ICPartitions.C_MULTILINE_COMMENT); 

dr= new DefaultDamagerRepairer(getStringScanner()); 
reconciler.setDamager(dr, ICPartitions.C_STRING); 
reconciler.setRepairer(dr, ICPartitions.C_STRING); 

dr= new DefaultDamagerRepairer(getStringScanner()); 
reconciler.setDamager(dr, ICPartitions.C_CHARACTER); 
reconciler.setRepairer(dr, ICPartitions.C_CHARACTER);

String language = ((CSourceViewer)sourceViewer).getDisplayLanguage();
if(language.equals(CEditor.LANGUAGE_CPP)) { 
    scanner= getCppCodeScanner(); 
} else {
    scanner= getCCodeScanner(); 
} 

dr= new DefaultDamagerRepairer(scanner);
reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);

Trước khi tiếp tục, chúng ta cần phải giải thích những gì mà cáctrình làm hỏngtrình sửa chữa thực hiện. Về cơ bản, mục đích của một lớp IPresentationDamager là để xác định những vùng của phân vùng tài liệu bị ảnh hưởng bởi DocumentEvent cho trước. Vì vậy, mặc dù mang tên có vẻ dữ tợn, trình làm hỏng thực sự chỉ là trình phân tích các hỏng hóc. IPresentationRepairer sử dụng kết quả của trình làm hỏng để tạo TextPresentation, chứa thông tin cần thiết để thay đổi màu sắc và kiểu dáng văn bản. Nói một cách đơn giản, DefaultDamagerRepairer thực hiện cả hai chức năng đó, trả lời một sự kiện bằng cách tạo ra TextPresentation đối với một phân vùng.

Trước khi CSourceViewer bắt đầu hoạt động, nó truy cập các đối tượng do lớp CSourceViewerConfiguration cung cấp và cài đặt PresentationReconciler. Việc cài đặt này cho phép trình điều hoà lắng nghe các lớp TextEvent. Các lớp này tương tự như các lớp DocumentEvent, trừ lớp TextEvent chứa các văn bản mới và các văn bản đã thay thế. Các DocumentEvent chỉ chứa văn bản mới.

Khi lớp PresentationReconciler nhận được TextEvent, nó xác định phân vùng nào chứa các văn bản thay đổi và cảnh báo lớp DefaultDamagerRepairer thích hợp. Mặc dù lớp điều hoà có lớp DefaultDamagerRepairer khác nhau cho từng phân vùng, DefaultDamagerRepairer thực hiện như nhau trong mọi trường hợp. Giống như FastPartitioner, nó quyết định điểm đầu của dòng đầu tiên có chứa các hỏng hóc và bắt đầu phân vùng. Số cực đại các điểm được coi là điểm đầu của hỏng hóc. Nó tìm thấy điểm cuối hỏng hóc bằng cách tính toán số tối thiểu của vị trí cuối cùng trong phân vùng và vị trí cuối cùng của văn bản không bị thay đổi trong phân vùng đó. Gói DefaultDamagerRepairer trả về IRegion, biểu diễn một đoạn của Document bằng cách cung cấp một khoảng trống và độ dài.

Bước 4. Xử lí quy tắc

Sau khi nhận được thông tin về hỏng hóc, PresentationReconciler lệnh cho DefaultDamagerRepairer của phân vùng tạo lớp TextPresentation mới và áp dụng nó cho khu vực bị hư hỏng. Như thể hiện trong liệt kê 1, mỗi DefaultDamagerRepairer được khởi tạo với trình quét phù hợp với phân vùng đó. Trình sửa chữa bắt đầu bằng cách lệnh cho trình quét của nó phân tích các khu vực bị hư hỏng và sinh ra các đánh dấu.

Trong CDT, các phân vùng bình luận được quét bởi CCommentScanner và các phân vùng ký tự và chuỗi được quét bởi SingleTokenCScanner. Phân vùng mặc định, chứa văn bản không thuộc phân vùng khác, được quét bởi hoặc CppCodeScanner hoặc CCodeScanner, tùy vào ngôn ngữ của mã. Mỗi lớp trong hai lớp này là lớp con của RuleBasedScanner, và nó sử dụng danh sách các IRuleTokenđể tạo ra dấu hiệu ứng với các khuôn mẫu phát hiện trong văn bản.

Các IRule này đặc biệt quan trọng, và API của trình soạn thảo văn bản của Eclipse cung cấp năm lớp triển khai thực hiện:

WordRule
trả về đánh dấu khi thấy các từ cụ thể
SingleLineRule
trả về đánh dấu khi thấy biểu thức trong dòng văn bản đơn
MultiLineRule
trả về đánh dấu khi thấy biểu thức trên nhiều dòng văn bản
NumberRule
trả về đánh dấu đối với mọi con số trong văn bản
WhitespaceRule
trả về đánh dấu khi phát hiện khoảng trắng

Để cung cấp cho bạn một ý tưởng về cách trình quét sử dụng các quy tắc này, liệt kê 2 cho thấy một đoạn mã, trong đó CCodeScanner tạo các quy tắc để phát hiện ra chuỗi và con số. Khi một phần văn bản khớp với một khuôn mẫu quy tắc, trình quét trả về một đánh dấu thích hợp.

Liệt kê 2. Thêm quy tắc vào RuleBasedScanner
Token token= getToken(ICColorConstants.C_STRING); 
rules.add(new SingleLineRule("'", "'", token, '\\')); 
token = getToken(ICColorConstants.C_NUMBER);
NumberRule numberRule = new NumberRule(token); 
rules.add(numberRule);

Các đánh dấu này chứa các chuỗi, giống như những chuỗi mà trình quét phân vùng trả về. Tuy nhiên, các chuỗi này thể hiện TextAttribute thay vì các tên của phân vùng. Lớp TextAttribute mô tả cách một đoạn đã cho của văn bản được thể hiện. Tức nó mô tả màu nền văn bản, màu nổi trên, và số nguyên đại diện cho kiểu dáng (SWT.BOLD, SWT.ITALIC, hoặcSWT.NORMAL). Ví dụ: CCodeScanner sử dụng lớp WordRule phát hiện từ khoá C/C++ trong văn bản. Khi lớp WordRule phát hiện ra một từ khóa, thì nội dung của đánh dấu của nó lệnh cho các trình soạn thảo cách từ khoá đó được hiển thị.

Khi DefaultDamagerRepairer nhận được một đánh dấu từ quy tắc, nó thu thập TextAttribute của đánh dấu và xác định cách hiển thị văn bản. Sau đó nó tạo ra đối tượng StyleRange điều khiển kiểu dáng đối với đoạn đã cho của văn bản Document. Sau khi trình sửa chữa bổ sung StyleRange vàoTextPresentation vai trò của nó kết thúc. Lớp PresentationReconciler gửi lớp TextPresentation đến bộ khung nhìn và bộ này cập nhật hướng dẫn từng bước StyledText với màu sắc và kiểu dáng phông chữ mới.


Cập nhật BBCDT

Giữa CDT và BBCDT, sự thay đổi lớn duy nhất trong mã kiểu dáng cú pháp là việc sử dụng các tham chiếu. Trong CDT, bạn có thể điều khiển màu sắc và kiểu dáng chữ của văn bản C/C++ bằng cách cập nhật tham chiếu trong bàn làm việc của Eclipse. Tuy nhiên, BBCDT không sử dụng tham chiếu. Vì vậy, để thay đổi kiểu dáng liên kết với từng chuỗi đánh dấu, bạn cần phải thay đổi mã trong trình kiến thiết của lớp CColorManager trong gói org.dworks.bbcdt.internal.ui.text .

Hình 2 cho thấy văn bản của BBCDT với các thiết lập mặc định.

Hình 2. Tạo kiểu dáng cú pháp của BBCDT
Tạo kiểu dáng cú pháp của BBCDT

Sau khi thêm các trình cắm thêm vào cài đặt Eclipse, bạn có thể tạo dự án BBCDT bằng cách nhấn File > New > Project và tùy chọn C hoặc C++. Để tạo một tệp, nhấn New > Other và tùy chọn C hoặcC++ .

Kết luận

Bài này giải thích cách động tác gõ phím có thể tạo một phân vùng Document mới hoặc thay đổi kiểu dáng văn bản của trình soạn thảo. Việc xử lý sự kiện không đơn giản, do việc phân tách của các thứ liên quan, bạn có thể tùy chỉnh bất kỳ khía cạnh nào mà không làm gián đoạn quá trình. Bây giờ chúng tôi đã giải thích cách làm việc của các sự kiện của trình soạn thảo văn bản Eclipse, chúng ta có thể thảo luận trong các bài sắp tới tính năng cao cấp mà phụ thuộc vào khả năng: phân tích cú pháp tự động của CDT.


Tải về

Mô tảTênKích thước
Part 2 source codeos-ecl-cdt2.zip552KB

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Nguồn mở
ArticleID=518118
ArticleTitle=Trình soạn thảo dựa trên CDT, Phần 2 : Trình bày văn bản trong CDT
publish-date=09102010