From 4e7070d2b16cd3f7a26b7040aacf880bc3fc7095 Mon Sep 17 00:00:00 2001 From: James Strachan Date: Wed, 28 Mar 2012 09:05:42 +0100 Subject: [PATCH] add syntax highlighting to included code in kdocs #KT-1660 Fixed --- .../doc/highlighter/SyntaxHighlighter.kt | 138 ++++++++++++++++++ .../jetbrains/kotlin/doc/model/KotlinModel.kt | 5 +- .../kotlin/doc/templates/ClassTemplate.kt | 2 +- .../doc/templates/PackageFrameTemplate.kt | 2 +- .../doc/templates/PackageSummaryTemplate.kt | 2 +- .../doc/templates/PackageTemplateSupport.kt | 5 + libraries/website/src/main/apidocs/kotlin.css | 74 ++++++++++ .../src/main/apidocs/resources/underline.gif | Bin 0 -> 815 bytes 8 files changed, 223 insertions(+), 5 deletions(-) create mode 100644 libraries/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/highlighter/SyntaxHighlighter.kt create mode 100644 libraries/website/src/main/apidocs/kotlin.css create mode 100644 libraries/website/src/main/apidocs/resources/underline.gif diff --git a/libraries/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/highlighter/SyntaxHighlighter.kt b/libraries/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/highlighter/SyntaxHighlighter.kt new file mode 100644 index 00000000000..955d339d670 --- /dev/null +++ b/libraries/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/highlighter/SyntaxHighlighter.kt @@ -0,0 +1,138 @@ +package org.jetbrains.kotlin.doc.highlighter + +import com.intellij.psi.* +import com.intellij.psi.tree.IElementType +import com.intellij.psi.tree.TokenSet +import java.util.HashMap +import java.util.Map +import kotlin.template.HtmlFormatter +import org.jetbrains.jet.lexer.* + +fun main(args: Array) { + val tool = SyntaxHighligher() + val answer = tool.highlight(""" val x = arrayList(1, 2, 3) + println("hello")""") + println(answer) +} + +/** + * Syntax highlights Kotlin code + */ +class SyntaxHighligher() { + var formatter: HtmlFormatter = HtmlFormatter() + val styleMap = createStyleMap() + + /** Highlights the given kotlin code as HTML */ + fun highlight(val code: String): String { + try { + val builder = StringBuilder() + builder.append( + "
" + + "
" + + "
" + ) + + // lets add the leading whitespace first + var idx = 0 + while (Character.isWhitespace(code[idx])) { + idx++ + } + if (idx > 0) { + val space = code.substring(0, idx) + builder.append("""$space""") + } + val lexer = JetLexer() + lexer.start(code) + val end = lexer.getTokenEnd() + while (true) { + lexer.advance() + val token = lexer.getTokenType() + if (token == null) break + val tokenText = lexer.getTokenSequence().toString().replaceAll("\n", "\r\n") + var style: String? = null + if (token is JetKeywordToken) { + style = "keyword" + } else if (token == JetTokens.IDENTIFIER) { + val types = JetTokens.SOFT_KEYWORDS?.getTypes() + if (types != null) { + for (softKeyword in types) { + if (softKeyword is JetKeywordToken) { + if (softKeyword.getValue().equals(tokenText)) { + style = "softkeyword" + break + } + } + } + } + style = if (style == null) "plain" else style + } else if (styleMap.containsKey(token)) { + style = styleMap.get(token) + if (style == null) { + println("Warning: No style for token $token") + } + } else { + style = "plain" + } + builder.append("""""") + formatter.format(builder, tokenText) + builder.append("") + } + + builder.append("
") + builder.append("
") + builder.append("
") + return builder.toString() ?: "" + } catch (e: Exception) { + println("Warning: failed to parse code $e") + val builder = StringBuilder() + builder.append("""
Jet highlighter error ["${e.javaClass.getSimpleName()}"]: """) + formatter.format(builder, e.getMessage()) + builder.append("
") + builder.append("Original text:") + builder.append("
")
+            formatter.format(builder, code)
+            builder.append("
") + builder.append("
") + return builder.toString() ?: "" + } + } + + + protected fun createStyleMap(): Map { + val styleMap = HashMap() + + fun putAll(tokenSet: TokenSet?, style: String): Unit { + if (tokenSet != null) { + for (token in tokenSet.getTypes().orEmpty()) { + styleMap.put(token, style) + } + } + } + + styleMap.put(JetTokens.BLOCK_COMMENT, "jet-comment") + styleMap.put(JetTokens.DOC_COMMENT, "jet-comment") + styleMap.put(JetTokens.EOL_COMMENT, "jet-comment") + styleMap.put(JetTokens.WHITE_SPACE, "whitespace") + styleMap.put(JetTokens.INTEGER_LITERAL, "number") + styleMap.put(JetTokens.FLOAT_LITERAL, "number") + styleMap.put(JetTokens.OPEN_QUOTE, "string") + styleMap.put(JetTokens.REGULAR_STRING_PART, "string") + styleMap.put(JetTokens.ESCAPE_SEQUENCE, "escape") + styleMap.put(JetTokens.LONG_TEMPLATE_ENTRY_START, "escape") + styleMap.put(JetTokens.LONG_TEMPLATE_ENTRY_END, "escape") + styleMap.put(JetTokens.SHORT_TEMPLATE_ENTRY_START, "escape") + styleMap.put(JetTokens.ESCAPE_SEQUENCE, "escape") + styleMap.put(JetTokens.CLOSING_QUOTE, "string") + styleMap.put(JetTokens.CHARACTER_LITERAL, "string") + styleMap.put(JetTokens.LABEL_IDENTIFIER, "label") + styleMap.put(JetTokens.ATAT, "label") + styleMap.put(JetTokens.FIELD_IDENTIFIER, "field") + styleMap.put(TokenType.BAD_CHARACTER, "bad") + putAll(JetTokens.STRINGS, "string") + putAll(JetTokens.MODIFIER_KEYWORDS, "softkeyword") + putAll(JetTokens.SOFT_KEYWORDS, "softkeyword") + putAll(JetTokens.COMMENTS, "jet-comment") + putAll(JetTokens.OPERATIONS, "operation") + return styleMap + } +} \ No newline at end of file diff --git a/libraries/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/model/KotlinModel.kt b/libraries/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/model/KotlinModel.kt index 71e80413c84..e5a005455e9 100644 --- a/libraries/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/model/KotlinModel.kt +++ b/libraries/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/model/KotlinModel.kt @@ -4,6 +4,7 @@ import kotlin.* import kotlin.util.* import org.jetbrains.kotlin.doc.KDocConfig +import org.jetbrains.kotlin.doc.highlighter.SyntaxHighligher import java.util.* import org.jetbrains.jet.lang.descriptors.ClassDescriptor @@ -180,6 +181,7 @@ class KModel(var context: BindingContext, val config: KDocConfig) { get() = packages.flatMap{ it.classes } public var markdownProcessor: PegDownProcessor = PegDownProcessor(Extensions.ALL) + public var highlighter: SyntaxHighligher = SyntaxHighligher() public val title: String get() = config.title @@ -399,8 +401,7 @@ class KModel(var context: BindingContext, val config: KDocConfig) { val fnName = words[1].sure() val content = findFunctionInclude(psiElement, includeFile, fnName) if (content != null) { - // TODO ideally we'd use pygmentize or somehting here to format the Kotlin code... - text = "
" + content + "
\n" + text = highlighter.highlight(content) } else { warning("could not find function $fnName in file $includeFile from source file ${psiElement.getContainingFile()}") } diff --git a/libraries/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/templates/ClassTemplate.kt b/libraries/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/templates/ClassTemplate.kt index e5b828ac975..7227c04fdc0 100644 --- a/libraries/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/templates/ClassTemplate.kt +++ b/libraries/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/templates/ClassTemplate.kt @@ -28,7 +28,7 @@ ${pageTitle()} - +${stylesheets()}