You've got at least two alternatives in XSLT.
Firstly you could wrap the text in the text you want to output in <xsl:text> elements. Whitespace between the end tag of one <xsl:text> and the start of another might be ignored.
The reason for the newlines being left in is because whitespace next to a text node (not a <xsl:text> node) is considered significant, so newline followed by a square bracket the new line is kept.
If you wrap the square bracket in something else (i.e. a <xsl:text> element) then the newline on its own becomes insignificant and will be dropped.
Code:
[<xsl:sequence select="something"></xsl:sequence>
<xsl:text>[]</xsl:text>
<xsl:text>[["textstuff" []] ]</xsl:text>
<xsl:text>[</xsl:text><xsl:for-each select="$varname/att"><some work here></xsl:for-each><xsl:text>]</xsl:text>
...
Still going to make you XSLT harder to read but will work.
Alternatively you can do as you say above and store in a temporary variable and then process again (as you are using a XSLT 2.0 compliant xslt processor).
Code:
<xsl:variable name="temp">
[<xsl:sequence select="something"></xsl:sequence>
[]
[["textstuff" []]<xsl:text> </xsl:text>]
[<xsl:for-each select="$varname/att"><some work here></xsl:for-each>]
[<xsl:for-each select="$varname2/att"><some more work here></xsl:for-each>]
]
</xsl:variable>
<xsl:sequence select="normalize-space($temp)"/>
Normalize will only remove multiple whitespaces but will still leave a single space, so you may need to use translate instead.