public function highlight($scanner, $source, $settings = null)
    {
        $oldSettings = null;
        if ($settings !== null) {
            if (!is_array($settings)) {
                throw new Exception('Luminous internal error: Settings is not an array');
            }
            $oldSettings = clone $this->settings;
            foreach ($settings as $k => $v) {
                $this->settings->set($k, $v);
            }
        }
        $shouldResetLanguage = false;
        $this->cache = null;
        if (!is_string($source)) {
            throw new InvalidArgumentException('Non-string supplied for $source');
        }

        if (!($scanner instanceof Scanner)) {
            if (!is_string($scanner)) {
                throw new InvalidArgumentException('Non-string or LuminousScanner instance supplied for $scanner');
            }
            $code = $scanner;
            $scanner = $this->scanners->GetScanner($code);
            if ($scanner === null) {
                throw new Exception("No known scanner for '$code' and no default set");
            }
            $shouldResetLanguage = true;
            $this->language = $this->scanners->GetDescription($code);
        }
        $cacheHit = true;
        $out = null;
        if ($this->settings->cache) {
            $cacheId = $this->cacheId($scanner, $source);
            if ($this->settings->sqlFunction !== null) {
                $this->cache = new SqlCache($cacheId);
                $this->cache->setSqlFunction($this->settings->sqlFunction);
            } else {
                $this->cache = new FileSystemCache($cacheId);
            }
            $this->cache->setPurgeTime($this->settings->cacheAge);
            $out = $this->cache->read();
        }
        if ($out === null) {
            $cacheHit = false;
            $outRaw = $scanner->highlight($source);
            $formatter = $this->getFormatter();
            $out = $formatter->format($outRaw);
        }

        if ($this->settings->cache && !$cacheHit) {
            $this->cache->write($out);
        }
        if ($shouldResetLanguage) {
            $this->language = null;
        }
        if ($oldSettings !== null) {
            $this->settings = $oldSettings;
        }
        return $out;
    }